162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci#include <linux/bug.h> 362306a36Sopenharmony_ci#include <linux/compiler.h> 462306a36Sopenharmony_ci#include <linux/export.h> 562306a36Sopenharmony_ci#include <linux/percpu.h> 662306a36Sopenharmony_ci#include <linux/processor.h> 762306a36Sopenharmony_ci#include <linux/smp.h> 862306a36Sopenharmony_ci#include <linux/topology.h> 962306a36Sopenharmony_ci#include <linux/sched/clock.h> 1062306a36Sopenharmony_ci#include <asm/qspinlock.h> 1162306a36Sopenharmony_ci#include <asm/paravirt.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define MAX_NODES 4 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct qnode { 1662306a36Sopenharmony_ci struct qnode *next; 1762306a36Sopenharmony_ci struct qspinlock *lock; 1862306a36Sopenharmony_ci int cpu; 1962306a36Sopenharmony_ci int yield_cpu; 2062306a36Sopenharmony_ci u8 locked; /* 1 if lock acquired */ 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct qnodes { 2462306a36Sopenharmony_ci int count; 2562306a36Sopenharmony_ci struct qnode nodes[MAX_NODES]; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Tuning parameters */ 2962306a36Sopenharmony_cistatic int steal_spins __read_mostly = (1 << 5); 3062306a36Sopenharmony_cistatic int remote_steal_spins __read_mostly = (1 << 2); 3162306a36Sopenharmony_ci#if _Q_SPIN_TRY_LOCK_STEAL == 1 3262306a36Sopenharmony_cistatic const bool maybe_stealers = true; 3362306a36Sopenharmony_ci#else 3462306a36Sopenharmony_cistatic bool maybe_stealers __read_mostly = true; 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_cistatic int head_spins __read_mostly = (1 << 8); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic bool pv_yield_owner __read_mostly = true; 3962306a36Sopenharmony_cistatic bool pv_yield_allow_steal __read_mostly = false; 4062306a36Sopenharmony_cistatic bool pv_spin_on_preempted_owner __read_mostly = false; 4162306a36Sopenharmony_cistatic bool pv_sleepy_lock __read_mostly = true; 4262306a36Sopenharmony_cistatic bool pv_sleepy_lock_sticky __read_mostly = false; 4362306a36Sopenharmony_cistatic u64 pv_sleepy_lock_interval_ns __read_mostly = 0; 4462306a36Sopenharmony_cistatic int pv_sleepy_lock_factor __read_mostly = 256; 4562306a36Sopenharmony_cistatic bool pv_yield_prev __read_mostly = true; 4662306a36Sopenharmony_cistatic bool pv_yield_propagate_owner __read_mostly = true; 4762306a36Sopenharmony_cistatic bool pv_prod_head __read_mostly = false; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(struct qnodes, qnodes); 5062306a36Sopenharmony_cistatic DEFINE_PER_CPU_ALIGNED(u64, sleepy_lock_seen_clock); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#if _Q_SPIN_SPEC_BARRIER == 1 5362306a36Sopenharmony_ci#define spec_barrier() do { asm volatile("ori 31,31,0" ::: "memory"); } while (0) 5462306a36Sopenharmony_ci#else 5562306a36Sopenharmony_ci#define spec_barrier() do { } while (0) 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic __always_inline bool recently_sleepy(void) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci /* pv_sleepy_lock is true when this is called */ 6162306a36Sopenharmony_ci if (pv_sleepy_lock_interval_ns) { 6262306a36Sopenharmony_ci u64 seen = this_cpu_read(sleepy_lock_seen_clock); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (seen) { 6562306a36Sopenharmony_ci u64 delta = sched_clock() - seen; 6662306a36Sopenharmony_ci if (delta < pv_sleepy_lock_interval_ns) 6762306a36Sopenharmony_ci return true; 6862306a36Sopenharmony_ci this_cpu_write(sleepy_lock_seen_clock, 0); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return false; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic __always_inline int get_steal_spins(bool paravirt, bool sleepy) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci if (paravirt && sleepy) 7862306a36Sopenharmony_ci return steal_spins * pv_sleepy_lock_factor; 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci return steal_spins; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic __always_inline int get_remote_steal_spins(bool paravirt, bool sleepy) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci if (paravirt && sleepy) 8662306a36Sopenharmony_ci return remote_steal_spins * pv_sleepy_lock_factor; 8762306a36Sopenharmony_ci else 8862306a36Sopenharmony_ci return remote_steal_spins; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic __always_inline int get_head_spins(bool paravirt, bool sleepy) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci if (paravirt && sleepy) 9462306a36Sopenharmony_ci return head_spins * pv_sleepy_lock_factor; 9562306a36Sopenharmony_ci else 9662306a36Sopenharmony_ci return head_spins; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline u32 encode_tail_cpu(int cpu) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci return (cpu + 1) << _Q_TAIL_CPU_OFFSET; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic inline int decode_tail_cpu(u32 val) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci return (val >> _Q_TAIL_CPU_OFFSET) - 1; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic inline int get_owner_cpu(u32 val) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci return (val & _Q_OWNER_CPU_MASK) >> _Q_OWNER_CPU_OFFSET; 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * Try to acquire the lock if it was not already locked. If the tail matches 11662306a36Sopenharmony_ci * mytail then clear it, otherwise leave it unchnaged. Return previous value. 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * This is used by the head of the queue to acquire the lock and clean up 11962306a36Sopenharmony_ci * its tail if it was the last one queued. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic __always_inline u32 trylock_clean_tail(struct qspinlock *lock, u32 tail) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci u32 newval = queued_spin_encode_locked_val(); 12462306a36Sopenharmony_ci u32 prev, tmp; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci asm volatile( 12762306a36Sopenharmony_ci"1: lwarx %0,0,%2,%7 # trylock_clean_tail \n" 12862306a36Sopenharmony_ci /* This test is necessary if there could be stealers */ 12962306a36Sopenharmony_ci" andi. %1,%0,%5 \n" 13062306a36Sopenharmony_ci" bne 3f \n" 13162306a36Sopenharmony_ci /* Test whether the lock tail == mytail */ 13262306a36Sopenharmony_ci" and %1,%0,%6 \n" 13362306a36Sopenharmony_ci" cmpw 0,%1,%3 \n" 13462306a36Sopenharmony_ci /* Merge the new locked value */ 13562306a36Sopenharmony_ci" or %1,%1,%4 \n" 13662306a36Sopenharmony_ci" bne 2f \n" 13762306a36Sopenharmony_ci /* If the lock tail matched, then clear it, otherwise leave it. */ 13862306a36Sopenharmony_ci" andc %1,%1,%6 \n" 13962306a36Sopenharmony_ci"2: stwcx. %1,0,%2 \n" 14062306a36Sopenharmony_ci" bne- 1b \n" 14162306a36Sopenharmony_ci"\t" PPC_ACQUIRE_BARRIER " \n" 14262306a36Sopenharmony_ci"3: \n" 14362306a36Sopenharmony_ci : "=&r" (prev), "=&r" (tmp) 14462306a36Sopenharmony_ci : "r" (&lock->val), "r"(tail), "r" (newval), 14562306a36Sopenharmony_ci "i" (_Q_LOCKED_VAL), 14662306a36Sopenharmony_ci "r" (_Q_TAIL_CPU_MASK), 14762306a36Sopenharmony_ci "i" (_Q_SPIN_EH_HINT) 14862306a36Sopenharmony_ci : "cr0", "memory"); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return prev; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Publish our tail, replacing previous tail. Return previous value. 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * This provides a release barrier for publishing node, this pairs with the 15762306a36Sopenharmony_ci * acquire barrier in get_tail_qnode() when the next CPU finds this tail 15862306a36Sopenharmony_ci * value. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_cistatic __always_inline u32 publish_tail_cpu(struct qspinlock *lock, u32 tail) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 prev, tmp; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci kcsan_release(); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci asm volatile( 16762306a36Sopenharmony_ci"\t" PPC_RELEASE_BARRIER " \n" 16862306a36Sopenharmony_ci"1: lwarx %0,0,%2 # publish_tail_cpu \n" 16962306a36Sopenharmony_ci" andc %1,%0,%4 \n" 17062306a36Sopenharmony_ci" or %1,%1,%3 \n" 17162306a36Sopenharmony_ci" stwcx. %1,0,%2 \n" 17262306a36Sopenharmony_ci" bne- 1b \n" 17362306a36Sopenharmony_ci : "=&r" (prev), "=&r"(tmp) 17462306a36Sopenharmony_ci : "r" (&lock->val), "r" (tail), "r"(_Q_TAIL_CPU_MASK) 17562306a36Sopenharmony_ci : "cr0", "memory"); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return prev; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic __always_inline u32 set_mustq(struct qspinlock *lock) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci u32 prev; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci asm volatile( 18562306a36Sopenharmony_ci"1: lwarx %0,0,%1 # set_mustq \n" 18662306a36Sopenharmony_ci" or %0,%0,%2 \n" 18762306a36Sopenharmony_ci" stwcx. %0,0,%1 \n" 18862306a36Sopenharmony_ci" bne- 1b \n" 18962306a36Sopenharmony_ci : "=&r" (prev) 19062306a36Sopenharmony_ci : "r" (&lock->val), "r" (_Q_MUST_Q_VAL) 19162306a36Sopenharmony_ci : "cr0", "memory"); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return prev; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic __always_inline u32 clear_mustq(struct qspinlock *lock) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci u32 prev; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci asm volatile( 20162306a36Sopenharmony_ci"1: lwarx %0,0,%1 # clear_mustq \n" 20262306a36Sopenharmony_ci" andc %0,%0,%2 \n" 20362306a36Sopenharmony_ci" stwcx. %0,0,%1 \n" 20462306a36Sopenharmony_ci" bne- 1b \n" 20562306a36Sopenharmony_ci : "=&r" (prev) 20662306a36Sopenharmony_ci : "r" (&lock->val), "r" (_Q_MUST_Q_VAL) 20762306a36Sopenharmony_ci : "cr0", "memory"); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return prev; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic __always_inline bool try_set_sleepy(struct qspinlock *lock, u32 old) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci u32 prev; 21562306a36Sopenharmony_ci u32 new = old | _Q_SLEEPY_VAL; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci BUG_ON(!(old & _Q_LOCKED_VAL)); 21862306a36Sopenharmony_ci BUG_ON(old & _Q_SLEEPY_VAL); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci asm volatile( 22162306a36Sopenharmony_ci"1: lwarx %0,0,%1 # try_set_sleepy \n" 22262306a36Sopenharmony_ci" cmpw 0,%0,%2 \n" 22362306a36Sopenharmony_ci" bne- 2f \n" 22462306a36Sopenharmony_ci" stwcx. %3,0,%1 \n" 22562306a36Sopenharmony_ci" bne- 1b \n" 22662306a36Sopenharmony_ci"2: \n" 22762306a36Sopenharmony_ci : "=&r" (prev) 22862306a36Sopenharmony_ci : "r" (&lock->val), "r"(old), "r" (new) 22962306a36Sopenharmony_ci : "cr0", "memory"); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return likely(prev == old); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic __always_inline void seen_sleepy_owner(struct qspinlock *lock, u32 val) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci if (pv_sleepy_lock) { 23762306a36Sopenharmony_ci if (pv_sleepy_lock_interval_ns) 23862306a36Sopenharmony_ci this_cpu_write(sleepy_lock_seen_clock, sched_clock()); 23962306a36Sopenharmony_ci if (!(val & _Q_SLEEPY_VAL)) 24062306a36Sopenharmony_ci try_set_sleepy(lock, val); 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic __always_inline void seen_sleepy_lock(void) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (pv_sleepy_lock && pv_sleepy_lock_interval_ns) 24762306a36Sopenharmony_ci this_cpu_write(sleepy_lock_seen_clock, sched_clock()); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic __always_inline void seen_sleepy_node(struct qspinlock *lock, u32 val) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci if (pv_sleepy_lock) { 25362306a36Sopenharmony_ci if (pv_sleepy_lock_interval_ns) 25462306a36Sopenharmony_ci this_cpu_write(sleepy_lock_seen_clock, sched_clock()); 25562306a36Sopenharmony_ci if (val & _Q_LOCKED_VAL) { 25662306a36Sopenharmony_ci if (!(val & _Q_SLEEPY_VAL)) 25762306a36Sopenharmony_ci try_set_sleepy(lock, val); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic struct qnode *get_tail_qnode(struct qspinlock *lock, u32 val) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci int cpu = decode_tail_cpu(val); 26562306a36Sopenharmony_ci struct qnodes *qnodesp = per_cpu_ptr(&qnodes, cpu); 26662306a36Sopenharmony_ci int idx; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * After publishing the new tail and finding a previous tail in the 27062306a36Sopenharmony_ci * previous val (which is the control dependency), this barrier 27162306a36Sopenharmony_ci * orders the release barrier in publish_tail_cpu performed by the 27262306a36Sopenharmony_ci * last CPU, with subsequently looking at its qnode structures 27362306a36Sopenharmony_ci * after the barrier. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci smp_acquire__after_ctrl_dep(); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci for (idx = 0; idx < MAX_NODES; idx++) { 27862306a36Sopenharmony_ci struct qnode *qnode = &qnodesp->nodes[idx]; 27962306a36Sopenharmony_ci if (qnode->lock == lock) 28062306a36Sopenharmony_ci return qnode; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci BUG(); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* Called inside spin_begin(). Returns whether or not the vCPU was preempted. */ 28762306a36Sopenharmony_cistatic __always_inline bool __yield_to_locked_owner(struct qspinlock *lock, u32 val, bool paravirt, bool mustq) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci int owner; 29062306a36Sopenharmony_ci u32 yield_count; 29162306a36Sopenharmony_ci bool preempted = false; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci BUG_ON(!(val & _Q_LOCKED_VAL)); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!paravirt) 29662306a36Sopenharmony_ci goto relax; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!pv_yield_owner) 29962306a36Sopenharmony_ci goto relax; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci owner = get_owner_cpu(val); 30262306a36Sopenharmony_ci yield_count = yield_count_of(owner); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if ((yield_count & 1) == 0) 30562306a36Sopenharmony_ci goto relax; /* owner vcpu is running */ 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci spin_end(); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci seen_sleepy_owner(lock, val); 31062306a36Sopenharmony_ci preempted = true; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * Read the lock word after sampling the yield count. On the other side 31462306a36Sopenharmony_ci * there may a wmb because the yield count update is done by the 31562306a36Sopenharmony_ci * hypervisor preemption and the value update by the OS, however this 31662306a36Sopenharmony_ci * ordering might reduce the chance of out of order accesses and 31762306a36Sopenharmony_ci * improve the heuristic. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci smp_rmb(); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (READ_ONCE(lock->val) == val) { 32262306a36Sopenharmony_ci if (mustq) 32362306a36Sopenharmony_ci clear_mustq(lock); 32462306a36Sopenharmony_ci yield_to_preempted(owner, yield_count); 32562306a36Sopenharmony_ci if (mustq) 32662306a36Sopenharmony_ci set_mustq(lock); 32762306a36Sopenharmony_ci spin_begin(); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Don't relax if we yielded. Maybe we should? */ 33062306a36Sopenharmony_ci return preempted; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci spin_begin(); 33362306a36Sopenharmony_cirelax: 33462306a36Sopenharmony_ci spin_cpu_relax(); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return preempted; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* Called inside spin_begin(). Returns whether or not the vCPU was preempted. */ 34062306a36Sopenharmony_cistatic __always_inline bool yield_to_locked_owner(struct qspinlock *lock, u32 val, bool paravirt) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci return __yield_to_locked_owner(lock, val, paravirt, false); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* Called inside spin_begin(). Returns whether or not the vCPU was preempted. */ 34662306a36Sopenharmony_cistatic __always_inline bool yield_head_to_locked_owner(struct qspinlock *lock, u32 val, bool paravirt) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci bool mustq = false; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if ((val & _Q_MUST_Q_VAL) && pv_yield_allow_steal) 35162306a36Sopenharmony_ci mustq = true; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return __yield_to_locked_owner(lock, val, paravirt, mustq); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic __always_inline void propagate_yield_cpu(struct qnode *node, u32 val, int *set_yield_cpu, bool paravirt) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct qnode *next; 35962306a36Sopenharmony_ci int owner; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!paravirt) 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci if (!pv_yield_propagate_owner) 36462306a36Sopenharmony_ci return; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci owner = get_owner_cpu(val); 36762306a36Sopenharmony_ci if (*set_yield_cpu == owner) 36862306a36Sopenharmony_ci return; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci next = READ_ONCE(node->next); 37162306a36Sopenharmony_ci if (!next) 37262306a36Sopenharmony_ci return; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (vcpu_is_preempted(owner)) { 37562306a36Sopenharmony_ci next->yield_cpu = owner; 37662306a36Sopenharmony_ci *set_yield_cpu = owner; 37762306a36Sopenharmony_ci } else if (*set_yield_cpu != -1) { 37862306a36Sopenharmony_ci next->yield_cpu = owner; 37962306a36Sopenharmony_ci *set_yield_cpu = owner; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* Called inside spin_begin() */ 38462306a36Sopenharmony_cistatic __always_inline bool yield_to_prev(struct qspinlock *lock, struct qnode *node, u32 val, bool paravirt) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci int prev_cpu = decode_tail_cpu(val); 38762306a36Sopenharmony_ci u32 yield_count; 38862306a36Sopenharmony_ci int yield_cpu; 38962306a36Sopenharmony_ci bool preempted = false; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (!paravirt) 39262306a36Sopenharmony_ci goto relax; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!pv_yield_propagate_owner) 39562306a36Sopenharmony_ci goto yield_prev; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci yield_cpu = READ_ONCE(node->yield_cpu); 39862306a36Sopenharmony_ci if (yield_cpu == -1) { 39962306a36Sopenharmony_ci /* Propagate back the -1 CPU */ 40062306a36Sopenharmony_ci if (node->next && node->next->yield_cpu != -1) 40162306a36Sopenharmony_ci node->next->yield_cpu = yield_cpu; 40262306a36Sopenharmony_ci goto yield_prev; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci yield_count = yield_count_of(yield_cpu); 40662306a36Sopenharmony_ci if ((yield_count & 1) == 0) 40762306a36Sopenharmony_ci goto yield_prev; /* owner vcpu is running */ 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (get_owner_cpu(READ_ONCE(lock->val)) != yield_cpu) 41062306a36Sopenharmony_ci goto yield_prev; /* re-sample lock owner */ 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci spin_end(); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci preempted = true; 41562306a36Sopenharmony_ci seen_sleepy_node(lock, val); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci smp_rmb(); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (yield_cpu == node->yield_cpu) { 42062306a36Sopenharmony_ci if (node->next && node->next->yield_cpu != yield_cpu) 42162306a36Sopenharmony_ci node->next->yield_cpu = yield_cpu; 42262306a36Sopenharmony_ci yield_to_preempted(yield_cpu, yield_count); 42362306a36Sopenharmony_ci spin_begin(); 42462306a36Sopenharmony_ci return preempted; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci spin_begin(); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciyield_prev: 42962306a36Sopenharmony_ci if (!pv_yield_prev) 43062306a36Sopenharmony_ci goto relax; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci yield_count = yield_count_of(prev_cpu); 43362306a36Sopenharmony_ci if ((yield_count & 1) == 0) 43462306a36Sopenharmony_ci goto relax; /* owner vcpu is running */ 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci spin_end(); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci preempted = true; 43962306a36Sopenharmony_ci seen_sleepy_node(lock, val); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci smp_rmb(); /* See __yield_to_locked_owner comment */ 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci if (!READ_ONCE(node->locked)) { 44462306a36Sopenharmony_ci yield_to_preempted(prev_cpu, yield_count); 44562306a36Sopenharmony_ci spin_begin(); 44662306a36Sopenharmony_ci return preempted; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci spin_begin(); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cirelax: 45162306a36Sopenharmony_ci spin_cpu_relax(); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return preempted; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic __always_inline bool steal_break(u32 val, int iters, bool paravirt, bool sleepy) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci if (iters >= get_steal_spins(paravirt, sleepy)) 45962306a36Sopenharmony_ci return true; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NUMA) && 46262306a36Sopenharmony_ci (iters >= get_remote_steal_spins(paravirt, sleepy))) { 46362306a36Sopenharmony_ci int cpu = get_owner_cpu(val); 46462306a36Sopenharmony_ci if (numa_node_id() != cpu_to_node(cpu)) 46562306a36Sopenharmony_ci return true; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci return false; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic __always_inline bool try_to_steal_lock(struct qspinlock *lock, bool paravirt) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci bool seen_preempted = false; 47362306a36Sopenharmony_ci bool sleepy = false; 47462306a36Sopenharmony_ci int iters = 0; 47562306a36Sopenharmony_ci u32 val; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!steal_spins) { 47862306a36Sopenharmony_ci /* XXX: should spin_on_preempted_owner do anything here? */ 47962306a36Sopenharmony_ci return false; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Attempt to steal the lock */ 48362306a36Sopenharmony_ci spin_begin(); 48462306a36Sopenharmony_ci do { 48562306a36Sopenharmony_ci bool preempted = false; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci val = READ_ONCE(lock->val); 48862306a36Sopenharmony_ci if (val & _Q_MUST_Q_VAL) 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci spec_barrier(); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (unlikely(!(val & _Q_LOCKED_VAL))) { 49362306a36Sopenharmony_ci spin_end(); 49462306a36Sopenharmony_ci if (__queued_spin_trylock_steal(lock)) 49562306a36Sopenharmony_ci return true; 49662306a36Sopenharmony_ci spin_begin(); 49762306a36Sopenharmony_ci } else { 49862306a36Sopenharmony_ci preempted = yield_to_locked_owner(lock, val, paravirt); 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (paravirt && pv_sleepy_lock) { 50262306a36Sopenharmony_ci if (!sleepy) { 50362306a36Sopenharmony_ci if (val & _Q_SLEEPY_VAL) { 50462306a36Sopenharmony_ci seen_sleepy_lock(); 50562306a36Sopenharmony_ci sleepy = true; 50662306a36Sopenharmony_ci } else if (recently_sleepy()) { 50762306a36Sopenharmony_ci sleepy = true; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci if (pv_sleepy_lock_sticky && seen_preempted && 51162306a36Sopenharmony_ci !(val & _Q_SLEEPY_VAL)) { 51262306a36Sopenharmony_ci if (try_set_sleepy(lock, val)) 51362306a36Sopenharmony_ci val |= _Q_SLEEPY_VAL; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (preempted) { 51862306a36Sopenharmony_ci seen_preempted = true; 51962306a36Sopenharmony_ci sleepy = true; 52062306a36Sopenharmony_ci if (!pv_spin_on_preempted_owner) 52162306a36Sopenharmony_ci iters++; 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * pv_spin_on_preempted_owner don't increase iters 52462306a36Sopenharmony_ci * while the owner is preempted -- we won't interfere 52562306a36Sopenharmony_ci * with it by definition. This could introduce some 52662306a36Sopenharmony_ci * latency issue if we continually observe preempted 52762306a36Sopenharmony_ci * owners, but hopefully that's a rare corner case of 52862306a36Sopenharmony_ci * a badly oversubscribed system. 52962306a36Sopenharmony_ci */ 53062306a36Sopenharmony_ci } else { 53162306a36Sopenharmony_ci iters++; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci } while (!steal_break(val, iters, paravirt, sleepy)); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci spin_end(); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return false; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic __always_inline void queued_spin_lock_mcs_queue(struct qspinlock *lock, bool paravirt) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct qnodes *qnodesp; 54362306a36Sopenharmony_ci struct qnode *next, *node; 54462306a36Sopenharmony_ci u32 val, old, tail; 54562306a36Sopenharmony_ci bool seen_preempted = false; 54662306a36Sopenharmony_ci bool sleepy = false; 54762306a36Sopenharmony_ci bool mustq = false; 54862306a36Sopenharmony_ci int idx; 54962306a36Sopenharmony_ci int set_yield_cpu = -1; 55062306a36Sopenharmony_ci int iters = 0; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci BUILD_BUG_ON(CONFIG_NR_CPUS >= (1U << _Q_TAIL_CPU_BITS)); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci qnodesp = this_cpu_ptr(&qnodes); 55562306a36Sopenharmony_ci if (unlikely(qnodesp->count >= MAX_NODES)) { 55662306a36Sopenharmony_ci spec_barrier(); 55762306a36Sopenharmony_ci while (!queued_spin_trylock(lock)) 55862306a36Sopenharmony_ci cpu_relax(); 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci idx = qnodesp->count++; 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * Ensure that we increment the head node->count before initialising 56562306a36Sopenharmony_ci * the actual node. If the compiler is kind enough to reorder these 56662306a36Sopenharmony_ci * stores, then an IRQ could overwrite our assignments. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci barrier(); 56962306a36Sopenharmony_ci node = &qnodesp->nodes[idx]; 57062306a36Sopenharmony_ci node->next = NULL; 57162306a36Sopenharmony_ci node->lock = lock; 57262306a36Sopenharmony_ci node->cpu = smp_processor_id(); 57362306a36Sopenharmony_ci node->yield_cpu = -1; 57462306a36Sopenharmony_ci node->locked = 0; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci tail = encode_tail_cpu(node->cpu); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * Assign all attributes of a node before it can be published. 58062306a36Sopenharmony_ci * Issues an lwsync, serving as a release barrier, as well as a 58162306a36Sopenharmony_ci * compiler barrier. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci old = publish_tail_cpu(lock, tail); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* 58662306a36Sopenharmony_ci * If there was a previous node; link it and wait until reaching the 58762306a36Sopenharmony_ci * head of the waitqueue. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_ci if (old & _Q_TAIL_CPU_MASK) { 59062306a36Sopenharmony_ci struct qnode *prev = get_tail_qnode(lock, old); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Link @node into the waitqueue. */ 59362306a36Sopenharmony_ci WRITE_ONCE(prev->next, node); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* Wait for mcs node lock to be released */ 59662306a36Sopenharmony_ci spin_begin(); 59762306a36Sopenharmony_ci while (!READ_ONCE(node->locked)) { 59862306a36Sopenharmony_ci spec_barrier(); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (yield_to_prev(lock, node, old, paravirt)) 60162306a36Sopenharmony_ci seen_preempted = true; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci spec_barrier(); 60462306a36Sopenharmony_ci spin_end(); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* Clear out stale propagated yield_cpu */ 60762306a36Sopenharmony_ci if (paravirt && pv_yield_propagate_owner && node->yield_cpu != -1) 60862306a36Sopenharmony_ci node->yield_cpu = -1; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci smp_rmb(); /* acquire barrier for the mcs lock */ 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci * Generic qspinlocks have this prefetch here, but it seems 61462306a36Sopenharmony_ci * like it could cause additional line transitions because 61562306a36Sopenharmony_ci * the waiter will keep loading from it. 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci if (_Q_SPIN_PREFETCH_NEXT) { 61862306a36Sopenharmony_ci next = READ_ONCE(node->next); 61962306a36Sopenharmony_ci if (next) 62062306a36Sopenharmony_ci prefetchw(next); 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* We're at the head of the waitqueue, wait for the lock. */ 62562306a36Sopenharmony_ciagain: 62662306a36Sopenharmony_ci spin_begin(); 62762306a36Sopenharmony_ci for (;;) { 62862306a36Sopenharmony_ci bool preempted; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci val = READ_ONCE(lock->val); 63162306a36Sopenharmony_ci if (!(val & _Q_LOCKED_VAL)) 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci spec_barrier(); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (paravirt && pv_sleepy_lock && maybe_stealers) { 63662306a36Sopenharmony_ci if (!sleepy) { 63762306a36Sopenharmony_ci if (val & _Q_SLEEPY_VAL) { 63862306a36Sopenharmony_ci seen_sleepy_lock(); 63962306a36Sopenharmony_ci sleepy = true; 64062306a36Sopenharmony_ci } else if (recently_sleepy()) { 64162306a36Sopenharmony_ci sleepy = true; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci if (pv_sleepy_lock_sticky && seen_preempted && 64562306a36Sopenharmony_ci !(val & _Q_SLEEPY_VAL)) { 64662306a36Sopenharmony_ci if (try_set_sleepy(lock, val)) 64762306a36Sopenharmony_ci val |= _Q_SLEEPY_VAL; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci propagate_yield_cpu(node, val, &set_yield_cpu, paravirt); 65262306a36Sopenharmony_ci preempted = yield_head_to_locked_owner(lock, val, paravirt); 65362306a36Sopenharmony_ci if (!maybe_stealers) 65462306a36Sopenharmony_ci continue; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (preempted) 65762306a36Sopenharmony_ci seen_preempted = true; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (paravirt && preempted) { 66062306a36Sopenharmony_ci sleepy = true; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (!pv_spin_on_preempted_owner) 66362306a36Sopenharmony_ci iters++; 66462306a36Sopenharmony_ci } else { 66562306a36Sopenharmony_ci iters++; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci if (!mustq && iters >= get_head_spins(paravirt, sleepy)) { 66962306a36Sopenharmony_ci mustq = true; 67062306a36Sopenharmony_ci set_mustq(lock); 67162306a36Sopenharmony_ci val |= _Q_MUST_Q_VAL; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci spec_barrier(); 67562306a36Sopenharmony_ci spin_end(); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* If we're the last queued, must clean up the tail. */ 67862306a36Sopenharmony_ci old = trylock_clean_tail(lock, tail); 67962306a36Sopenharmony_ci if (unlikely(old & _Q_LOCKED_VAL)) { 68062306a36Sopenharmony_ci BUG_ON(!maybe_stealers); 68162306a36Sopenharmony_ci goto again; /* Can only be true if maybe_stealers. */ 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if ((old & _Q_TAIL_CPU_MASK) == tail) 68562306a36Sopenharmony_ci goto release; /* We were the tail, no next. */ 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* There is a next, must wait for node->next != NULL (MCS protocol) */ 68862306a36Sopenharmony_ci next = READ_ONCE(node->next); 68962306a36Sopenharmony_ci if (!next) { 69062306a36Sopenharmony_ci spin_begin(); 69162306a36Sopenharmony_ci while (!(next = READ_ONCE(node->next))) 69262306a36Sopenharmony_ci cpu_relax(); 69362306a36Sopenharmony_ci spin_end(); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci spec_barrier(); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * Unlock the next mcs waiter node. Release barrier is not required 69962306a36Sopenharmony_ci * here because the acquirer is only accessing the lock word, and 70062306a36Sopenharmony_ci * the acquire barrier we took the lock with orders that update vs 70162306a36Sopenharmony_ci * this store to locked. The corresponding barrier is the smp_rmb() 70262306a36Sopenharmony_ci * acquire barrier for mcs lock, above. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci if (paravirt && pv_prod_head) { 70562306a36Sopenharmony_ci int next_cpu = next->cpu; 70662306a36Sopenharmony_ci WRITE_ONCE(next->locked, 1); 70762306a36Sopenharmony_ci if (_Q_SPIN_MISO) 70862306a36Sopenharmony_ci asm volatile("miso" ::: "memory"); 70962306a36Sopenharmony_ci if (vcpu_is_preempted(next_cpu)) 71062306a36Sopenharmony_ci prod_cpu(next_cpu); 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci WRITE_ONCE(next->locked, 1); 71362306a36Sopenharmony_ci if (_Q_SPIN_MISO) 71462306a36Sopenharmony_ci asm volatile("miso" ::: "memory"); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cirelease: 71862306a36Sopenharmony_ci qnodesp->count--; /* release the node */ 71962306a36Sopenharmony_ci} 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_civoid queued_spin_lock_slowpath(struct qspinlock *lock) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * This looks funny, but it induces the compiler to inline both 72562306a36Sopenharmony_ci * sides of the branch rather than share code as when the condition 72662306a36Sopenharmony_ci * is passed as the paravirt argument to the functions. 72762306a36Sopenharmony_ci */ 72862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_PARAVIRT_SPINLOCKS) && is_shared_processor()) { 72962306a36Sopenharmony_ci if (try_to_steal_lock(lock, true)) { 73062306a36Sopenharmony_ci spec_barrier(); 73162306a36Sopenharmony_ci return; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci queued_spin_lock_mcs_queue(lock, true); 73462306a36Sopenharmony_ci } else { 73562306a36Sopenharmony_ci if (try_to_steal_lock(lock, false)) { 73662306a36Sopenharmony_ci spec_barrier(); 73762306a36Sopenharmony_ci return; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci queued_spin_lock_mcs_queue(lock, false); 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ciEXPORT_SYMBOL(queued_spin_lock_slowpath); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci#ifdef CONFIG_PARAVIRT_SPINLOCKS 74562306a36Sopenharmony_civoid pv_spinlocks_init(void) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci#endif 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci#include <linux/debugfs.h> 75162306a36Sopenharmony_cistatic int steal_spins_set(void *data, u64 val) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci#if _Q_SPIN_TRY_LOCK_STEAL == 1 75462306a36Sopenharmony_ci /* MAYBE_STEAL remains true */ 75562306a36Sopenharmony_ci steal_spins = val; 75662306a36Sopenharmony_ci#else 75762306a36Sopenharmony_ci static DEFINE_MUTEX(lock); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * The lock slow path has a !maybe_stealers case that can assume 76162306a36Sopenharmony_ci * the head of queue will not see concurrent waiters. That waiter 76262306a36Sopenharmony_ci * is unsafe in the presence of stealers, so must keep them away 76362306a36Sopenharmony_ci * from one another. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci mutex_lock(&lock); 76762306a36Sopenharmony_ci if (val && !steal_spins) { 76862306a36Sopenharmony_ci maybe_stealers = true; 76962306a36Sopenharmony_ci /* wait for queue head waiter to go away */ 77062306a36Sopenharmony_ci synchronize_rcu(); 77162306a36Sopenharmony_ci steal_spins = val; 77262306a36Sopenharmony_ci } else if (!val && steal_spins) { 77362306a36Sopenharmony_ci steal_spins = val; 77462306a36Sopenharmony_ci /* wait for all possible stealers to go away */ 77562306a36Sopenharmony_ci synchronize_rcu(); 77662306a36Sopenharmony_ci maybe_stealers = false; 77762306a36Sopenharmony_ci } else { 77862306a36Sopenharmony_ci steal_spins = val; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci mutex_unlock(&lock); 78162306a36Sopenharmony_ci#endif 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci return 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic int steal_spins_get(void *data, u64 *val) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci *val = steal_spins; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_steal_spins, steal_spins_get, steal_spins_set, "%llu\n"); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic int remote_steal_spins_set(void *data, u64 val) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci remote_steal_spins = val; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return 0; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic int remote_steal_spins_get(void *data, u64 *val) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci *val = remote_steal_spins; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return 0; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_remote_steal_spins, remote_steal_spins_get, remote_steal_spins_set, "%llu\n"); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic int head_spins_set(void *data, u64 val) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci head_spins = val; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return 0; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int head_spins_get(void *data, u64 *val) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci *val = head_spins; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return 0; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_head_spins, head_spins_get, head_spins_set, "%llu\n"); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int pv_yield_owner_set(void *data, u64 val) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci pv_yield_owner = !!val; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return 0; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int pv_yield_owner_get(void *data, u64 *val) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci *val = pv_yield_owner; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_owner, pv_yield_owner_get, pv_yield_owner_set, "%llu\n"); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int pv_yield_allow_steal_set(void *data, u64 val) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci pv_yield_allow_steal = !!val; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int pv_yield_allow_steal_get(void *data, u64 *val) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci *val = pv_yield_allow_steal; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_allow_steal, pv_yield_allow_steal_get, pv_yield_allow_steal_set, "%llu\n"); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int pv_spin_on_preempted_owner_set(void *data, u64 val) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci pv_spin_on_preempted_owner = !!val; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int pv_spin_on_preempted_owner_get(void *data, u64 *val) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci *val = pv_spin_on_preempted_owner; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_spin_on_preempted_owner, pv_spin_on_preempted_owner_get, pv_spin_on_preempted_owner_set, "%llu\n"); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int pv_sleepy_lock_set(void *data, u64 val) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci pv_sleepy_lock = !!val; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int pv_sleepy_lock_get(void *data, u64 *val) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci *val = pv_sleepy_lock; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock, pv_sleepy_lock_get, pv_sleepy_lock_set, "%llu\n"); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int pv_sleepy_lock_sticky_set(void *data, u64 val) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci pv_sleepy_lock_sticky = !!val; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int pv_sleepy_lock_sticky_get(void *data, u64 *val) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci *val = pv_sleepy_lock_sticky; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return 0; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock_sticky, pv_sleepy_lock_sticky_get, pv_sleepy_lock_sticky_set, "%llu\n"); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int pv_sleepy_lock_interval_ns_set(void *data, u64 val) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci pv_sleepy_lock_interval_ns = val; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic int pv_sleepy_lock_interval_ns_get(void *data, u64 *val) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci *val = pv_sleepy_lock_interval_ns; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock_interval_ns, pv_sleepy_lock_interval_ns_get, pv_sleepy_lock_interval_ns_set, "%llu\n"); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_cistatic int pv_sleepy_lock_factor_set(void *data, u64 val) 92462306a36Sopenharmony_ci{ 92562306a36Sopenharmony_ci pv_sleepy_lock_factor = val; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic int pv_sleepy_lock_factor_get(void *data, u64 *val) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci *val = pv_sleepy_lock_factor; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci return 0; 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock_factor, pv_sleepy_lock_factor_get, pv_sleepy_lock_factor_set, "%llu\n"); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic int pv_yield_prev_set(void *data, u64 val) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci pv_yield_prev = !!val; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci return 0; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int pv_yield_prev_get(void *data, u64 *val) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci *val = pv_yield_prev; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_prev, pv_yield_prev_get, pv_yield_prev_set, "%llu\n"); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic int pv_yield_propagate_owner_set(void *data, u64 val) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci pv_yield_propagate_owner = !!val; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci return 0; 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_cistatic int pv_yield_propagate_owner_get(void *data, u64 *val) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci *val = pv_yield_propagate_owner; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_propagate_owner, pv_yield_propagate_owner_get, pv_yield_propagate_owner_set, "%llu\n"); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic int pv_prod_head_set(void *data, u64 val) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci pv_prod_head = !!val; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci return 0; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic int pv_prod_head_get(void *data, u64 *val) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci *val = pv_prod_head; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci return 0; 98362306a36Sopenharmony_ci} 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ciDEFINE_SIMPLE_ATTRIBUTE(fops_pv_prod_head, pv_prod_head_get, pv_prod_head_set, "%llu\n"); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic __init int spinlock_debugfs_init(void) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci debugfs_create_file("qspl_steal_spins", 0600, arch_debugfs_dir, NULL, &fops_steal_spins); 99062306a36Sopenharmony_ci debugfs_create_file("qspl_remote_steal_spins", 0600, arch_debugfs_dir, NULL, &fops_remote_steal_spins); 99162306a36Sopenharmony_ci debugfs_create_file("qspl_head_spins", 0600, arch_debugfs_dir, NULL, &fops_head_spins); 99262306a36Sopenharmony_ci if (is_shared_processor()) { 99362306a36Sopenharmony_ci debugfs_create_file("qspl_pv_yield_owner", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_owner); 99462306a36Sopenharmony_ci debugfs_create_file("qspl_pv_yield_allow_steal", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_allow_steal); 99562306a36Sopenharmony_ci debugfs_create_file("qspl_pv_spin_on_preempted_owner", 0600, arch_debugfs_dir, NULL, &fops_pv_spin_on_preempted_owner); 99662306a36Sopenharmony_ci debugfs_create_file("qspl_pv_sleepy_lock", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock); 99762306a36Sopenharmony_ci debugfs_create_file("qspl_pv_sleepy_lock_sticky", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock_sticky); 99862306a36Sopenharmony_ci debugfs_create_file("qspl_pv_sleepy_lock_interval_ns", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock_interval_ns); 99962306a36Sopenharmony_ci debugfs_create_file("qspl_pv_sleepy_lock_factor", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock_factor); 100062306a36Sopenharmony_ci debugfs_create_file("qspl_pv_yield_prev", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_prev); 100162306a36Sopenharmony_ci debugfs_create_file("qspl_pv_yield_propagate_owner", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_propagate_owner); 100262306a36Sopenharmony_ci debugfs_create_file("qspl_pv_prod_head", 0600, arch_debugfs_dir, NULL, &fops_pv_prod_head); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_cidevice_initcall(spinlock_debugfs_init); 1008