File Index
Files and directories
Sign in
FILE

qspinlock.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/lib/qspinlock.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2#include <linux/bug.h>
  3#include <linux/compiler.h>
  4#include <linux/export.h>
  5#include <linux/percpu.h>
  6#include <linux/processor.h>
  7#include <linux/smp.h>
  8#include <linux/topology.h>
  9#include <linux/sched/clock.h>
 10#include <asm/qspinlock.h>
 11#include <asm/paravirt.h>
 12#include <trace/events/lock.h>
 13
 14#define MAX_NODES	4
 15
 16struct qnode {
 17	struct qnode	*next;
 18	struct qspinlock *lock;
 19	int		cpu;
 20	u8		sleepy; /* 1 if the previous vCPU was preempted or
 21				 * if the previous node was sleepy */
 22	u8		locked; /* 1 if lock acquired */
 23};
 24
 25struct qnodes {
 26	int		count;
 27	struct qnode nodes[MAX_NODES];
 28};
 29
 30/* Tuning parameters */
 31static int steal_spins __read_mostly = (1 << 5);
 32static int remote_steal_spins __read_mostly = (1 << 2);
 33#if _Q_SPIN_TRY_LOCK_STEAL == 1
 34static const bool maybe_stealers = true;
 35#else
 36static bool maybe_stealers __read_mostly = true;
 37#endif
 38static int head_spins __read_mostly = (1 << 8);
 39
 40static bool pv_yield_owner __read_mostly = true;
 41static bool pv_yield_allow_steal __read_mostly = false;
 42static bool pv_spin_on_preempted_owner __read_mostly = false;
 43static bool pv_sleepy_lock __read_mostly = true;
 44static bool pv_sleepy_lock_sticky __read_mostly = false;
 45static u64 pv_sleepy_lock_interval_ns __read_mostly = 0;
 46static int pv_sleepy_lock_factor __read_mostly = 256;
 47static bool pv_yield_prev __read_mostly = true;
 48static bool pv_yield_sleepy_owner __read_mostly = true;
 49static bool pv_prod_head __read_mostly = false;
 50
 51static DEFINE_PER_CPU_ALIGNED(struct qnodes, qnodes);
 52static DEFINE_PER_CPU_ALIGNED(u64, sleepy_lock_seen_clock);
 53
 54#if _Q_SPIN_SPEC_BARRIER == 1
 55#define spec_barrier() do { asm volatile("ori 31,31,0" ::: "memory"); } while (0)
 56#else
 57#define spec_barrier() do { } while (0)
 58#endif
 59
 60static __always_inline bool recently_sleepy(void)
 61{
 62	/* pv_sleepy_lock is true when this is called */
 63	if (pv_sleepy_lock_interval_ns) {
 64		u64 seen = this_cpu_read(sleepy_lock_seen_clock);
 65
 66		if (seen) {
 67			u64 delta = sched_clock() - seen;
 68			if (delta < pv_sleepy_lock_interval_ns)
 69				return true;
 70			this_cpu_write(sleepy_lock_seen_clock, 0);
 71		}
 72	}
 73
 74	return false;
 75}
 76
 77static __always_inline int get_steal_spins(bool paravirt, bool sleepy)
 78{
 79	if (paravirt && sleepy)
 80		return steal_spins * pv_sleepy_lock_factor;
 81	else
 82		return steal_spins;
 83}
 84
 85static __always_inline int get_remote_steal_spins(bool paravirt, bool sleepy)
 86{
 87	if (paravirt && sleepy)
 88		return remote_steal_spins * pv_sleepy_lock_factor;
 89	else
 90		return remote_steal_spins;
 91}
 92
 93static __always_inline int get_head_spins(bool paravirt, bool sleepy)
 94{
 95	if (paravirt && sleepy)
 96		return head_spins * pv_sleepy_lock_factor;
 97	else
 98		return head_spins;
 99}
100
101static inline u32 encode_tail_cpu(int cpu)
102{
103	return (cpu + 1) << _Q_TAIL_CPU_OFFSET;
104}
105
106static inline int decode_tail_cpu(u32 val)
107{
108	return (val >> _Q_TAIL_CPU_OFFSET) - 1;
109}
110
111static inline int get_owner_cpu(u32 val)
112{
113	return (val & _Q_OWNER_CPU_MASK) >> _Q_OWNER_CPU_OFFSET;
114}
115
116/*
117 * Try to acquire the lock if it was not already locked. If the tail matches
118 * mytail then clear it, otherwise leave it unchnaged. Return previous value.
119 *
120 * This is used by the head of the queue to acquire the lock and clean up
121 * its tail if it was the last one queued.
122 */
123static __always_inline u32 trylock_clean_tail(struct qspinlock *lock, u32 tail)
124{
125	u32 newval = queued_spin_encode_locked_val();
126	u32 prev, tmp;
127
128	asm volatile(
129"1:	lwarx	%0,0,%2,%7	# trylock_clean_tail			\n"
130	/* This test is necessary if there could be stealers */
131"	andi.	%1,%0,%5						\n"
132"	bne	3f							\n"
133	/* Test whether the lock tail == mytail */
134"	and	%1,%0,%6						\n"
135"	cmpw	0,%1,%3							\n"
136	/* Merge the new locked value */
137"	or	%1,%1,%4						\n"
138"	bne	2f							\n"
139	/* If the lock tail matched, then clear it, otherwise leave it. */
140"	andc	%1,%1,%6						\n"
141"2:	stwcx.	%1,0,%2							\n"
142"	bne-	1b							\n"
143"\t"	PPC_ACQUIRE_BARRIER "						\n"
144"3:									\n"
145	: "=&r" (prev), "=&r" (tmp)
146	: "r" (&lock->val), "r"(tail), "r" (newval),
147	  "i" (_Q_LOCKED_VAL),
148	  "r" (_Q_TAIL_CPU_MASK),
149	  "i" (_Q_SPIN_EH_HINT)
150	: "cr0", "memory");
151
152	return prev;
153}
154
155/*
156 * Publish our tail, replacing previous tail. Return previous value.
157 *
158 * This provides a release barrier for publishing node, this pairs with the
159 * acquire barrier in get_tail_qnode() when the next CPU finds this tail
160 * value.
161 */
162static __always_inline u32 publish_tail_cpu(struct qspinlock *lock, u32 tail)
163{
164	u32 prev, tmp;
165
166	kcsan_release();
167
168	asm volatile(
169"\t"	PPC_RELEASE_BARRIER "						\n"
170"1:	lwarx	%0,0,%2		# publish_tail_cpu			\n"
171"	andc	%1,%0,%4						\n"
172"	or	%1,%1,%3						\n"
173"	stwcx.	%1,0,%2							\n"
174"	bne-	1b							\n"
175	: "=&r" (prev), "=&r"(tmp)
176	: "r" (&lock->val), "r" (tail), "r"(_Q_TAIL_CPU_MASK)
177	: "cr0", "memory");
178
179	return prev;
180}
181
182static __always_inline u32 set_mustq(struct qspinlock *lock)
183{
184	u32 prev;
185
186	asm volatile(
187"1:	lwarx	%0,0,%1		# set_mustq				\n"
188"	or	%0,%0,%2						\n"
189"	stwcx.	%0,0,%1							\n"
190"	bne-	1b							\n"
191	: "=&r" (prev)
192	: "r" (&lock->val), "r" (_Q_MUST_Q_VAL)
193	: "cr0", "memory");
194
195	return prev;
196}
197
198static __always_inline u32 clear_mustq(struct qspinlock *lock)
199{
200	u32 prev;
201
202	asm volatile(
203"1:	lwarx	%0,0,%1		# clear_mustq				\n"
204"	andc	%0,%0,%2						\n"
205"	stwcx.	%0,0,%1							\n"
206"	bne-	1b							\n"
207	: "=&r" (prev)
208	: "r" (&lock->val), "r" (_Q_MUST_Q_VAL)
209	: "cr0", "memory");
210
211	return prev;
212}
213
214static __always_inline bool try_set_sleepy(struct qspinlock *lock, u32 old)
215{
216	u32 prev;
217	u32 new = old | _Q_SLEEPY_VAL;
218
219	BUG_ON(!(old & _Q_LOCKED_VAL));
220	BUG_ON(old & _Q_SLEEPY_VAL);
221
222	asm volatile(
223"1:	lwarx	%0,0,%1		# try_set_sleepy			\n"
224"	cmpw	0,%0,%2							\n"
225"	bne-	2f							\n"
226"	stwcx.	%3,0,%1							\n"
227"	bne-	1b							\n"
228"2:									\n"
229	: "=&r" (prev)
230	: "r" (&lock->val), "r"(old), "r" (new)
231	: "cr0", "memory");
232
233	return likely(prev == old);
234}
235
236static __always_inline void seen_sleepy_owner(struct qspinlock *lock, u32 val)
237{
238	if (pv_sleepy_lock) {
239		if (pv_sleepy_lock_interval_ns)
240			this_cpu_write(sleepy_lock_seen_clock, sched_clock());
241		if (!(val & _Q_SLEEPY_VAL))
242			try_set_sleepy(lock, val);
243	}
244}
245
246static __always_inline void seen_sleepy_lock(void)
247{
248	if (pv_sleepy_lock && pv_sleepy_lock_interval_ns)
249		this_cpu_write(sleepy_lock_seen_clock, sched_clock());
250}
251
252static __always_inline void seen_sleepy_node(void)
253{
254	if (pv_sleepy_lock) {
255		if (pv_sleepy_lock_interval_ns)
256			this_cpu_write(sleepy_lock_seen_clock, sched_clock());
257		/* Don't set sleepy because we likely have a stale val */
258	}
259}
260
261static struct qnode *get_tail_qnode(struct qspinlock *lock, int prev_cpu)
262{
263	struct qnodes *qnodesp = per_cpu_ptr(&qnodes, prev_cpu);
264	int idx;
265
266	/*
267	 * After publishing the new tail and finding a previous tail in the
268	 * previous val (which is the control dependency), this barrier
269	 * orders the release barrier in publish_tail_cpu performed by the
270	 * last CPU, with subsequently looking at its qnode structures
271	 * after the barrier.
272	 */
273	smp_acquire__after_ctrl_dep();
274
275	for (idx = 0; idx < MAX_NODES; idx++) {
276		struct qnode *qnode = &qnodesp->nodes[idx];
277		if (qnode->lock == lock)
278			return qnode;
279	}
280
281	BUG();
282}
283
284/* Called inside spin_begin(). Returns whether or not the vCPU was preempted. */
285static __always_inline bool __yield_to_locked_owner(struct qspinlock *lock, u32 val, bool paravirt, bool mustq)
286{
287	int owner;
288	u32 yield_count;
289	bool preempted = false;
290
291	BUG_ON(!(val & _Q_LOCKED_VAL));
292
293	if (!paravirt)
294		goto relax;
295
296	if (!pv_yield_owner)
297		goto relax;
298
299	owner = get_owner_cpu(val);
300	yield_count = yield_count_of(owner);
301
302	if ((yield_count & 1) == 0)
303		goto relax; /* owner vcpu is running */
304
305	spin_end();
306
307	seen_sleepy_owner(lock, val);
308	preempted = true;
309
310	/*
311	 * Read the lock word after sampling the yield count. On the other side
312	 * there may a wmb because the yield count update is done by the
313	 * hypervisor preemption and the value update by the OS, however this
314	 * ordering might reduce the chance of out of order accesses and
315	 * improve the heuristic.
316	 */
317	smp_rmb();
318
319	if (READ_ONCE(lock->val) == val) {
320		if (mustq)
321			clear_mustq(lock);
322		yield_to_preempted(owner, yield_count);
323		if (mustq)
324			set_mustq(lock);
325		spin_begin();
326
327		/* Don't relax if we yielded. Maybe we should? */
328		return preempted;
329	}
330	spin_begin();
331relax:
332	spin_cpu_relax();
333
334	return preempted;
335}
336
337/* Called inside spin_begin(). Returns whether or not the vCPU was preempted. */
338static __always_inline bool yield_to_locked_owner(struct qspinlock *lock, u32 val, bool paravirt)
339{
340	return __yield_to_locked_owner(lock, val, paravirt, false);
341}
342
343/* Called inside spin_begin(). Returns whether or not the vCPU was preempted. */
344static __always_inline bool yield_head_to_locked_owner(struct qspinlock *lock, u32 val, bool paravirt)
345{
346	bool mustq = false;
347
348	if ((val & _Q_MUST_Q_VAL) && pv_yield_allow_steal)
349		mustq = true;
350
351	return __yield_to_locked_owner(lock, val, paravirt, mustq);
352}
353
354static __always_inline void propagate_sleepy(struct qnode *node, u32 val, bool paravirt)
355{
356	struct qnode *next;
357	int owner;
358
359	if (!paravirt)
360		return;
361	if (!pv_yield_sleepy_owner)
362		return;
363
364	next = READ_ONCE(node->next);
365	if (!next)
366		return;
367
368	if (next->sleepy)
369		return;
370
371	owner = get_owner_cpu(val);
372	if (vcpu_is_preempted(owner))
373		next->sleepy = 1;
374}
375
376/* Called inside spin_begin() */
377static __always_inline bool yield_to_prev(struct qspinlock *lock, struct qnode *node, int prev_cpu, bool paravirt)
378{
379	u32 yield_count;
380	bool preempted = false;
381
382	if (!paravirt)
383		goto relax;
384
385	if (!pv_yield_sleepy_owner)
386		goto yield_prev;
387
388	/*
389	 * If the previous waiter was preempted it might not be able to
390	 * propagate sleepy to us, so check the lock in that case too.
391	 */
392	if (node->sleepy || vcpu_is_preempted(prev_cpu)) {
393		u32 val = READ_ONCE(lock->val);
394
395		if (val & _Q_LOCKED_VAL) {
396			if (node->next && !node->next->sleepy) {
397				/*
398				 * Propagate sleepy to next waiter. Only if
399				 * owner is preempted, which allows the queue
400				 * to become "non-sleepy" if vCPU preemption
401				 * ceases to occur, even if the lock remains
402				 * highly contended.
403				 */
404				if (vcpu_is_preempted(get_owner_cpu(val)))
405					node->next->sleepy = 1;
406			}
407
408			preempted = yield_to_locked_owner(lock, val, paravirt);
409			if (preempted)
410				return preempted;
411		}
412		node->sleepy = false;
413	}
414
415yield_prev:
416	if (!pv_yield_prev)
417		goto relax;
418
419	yield_count = yield_count_of(prev_cpu);
420	if ((yield_count & 1) == 0)
421		goto relax; /* owner vcpu is running */
422
423	spin_end();
424
425	preempted = true;
426	seen_sleepy_node();
427
428	smp_rmb(); /* See __yield_to_locked_owner comment */
429
430	if (!READ_ONCE(node->locked)) {
431		yield_to_preempted(prev_cpu, yield_count);
432		spin_begin();
433		return preempted;
434	}
435	spin_begin();
436
437relax:
438	spin_cpu_relax();
439
440	return preempted;
441}
442
443static __always_inline bool steal_break(u32 val, int iters, bool paravirt, bool sleepy)
444{
445	if (iters >= get_steal_spins(paravirt, sleepy))
446		return true;
447
448	if (IS_ENABLED(CONFIG_NUMA) &&
449	    (iters >= get_remote_steal_spins(paravirt, sleepy))) {
450		int cpu = get_owner_cpu(val);
451		if (numa_node_id() != cpu_to_node(cpu))
452			return true;
453	}
454	return false;
455}
456
457static __always_inline bool try_to_steal_lock(struct qspinlock *lock, bool paravirt)
458{
459	bool seen_preempted = false;
460	bool sleepy = false;
461	int iters = 0;
462	u32 val;
463
464	if (!steal_spins) {
465		/* XXX: should spin_on_preempted_owner do anything here? */
466		return false;
467	}
468
469	/* Attempt to steal the lock */
470	spin_begin();
471	do {
472		bool preempted = false;
473
474		val = READ_ONCE(lock->val);
475		if (val & _Q_MUST_Q_VAL)
476			break;
477		spec_barrier();
478
479		if (unlikely(!(val & _Q_LOCKED_VAL))) {
480			spin_end();
481			if (__queued_spin_trylock_steal(lock))
482				return true;
483			spin_begin();
484		} else {
485			preempted = yield_to_locked_owner(lock, val, paravirt);
486		}
487
488		if (paravirt && pv_sleepy_lock) {
489			if (!sleepy) {
490				if (val & _Q_SLEEPY_VAL) {
491					seen_sleepy_lock();
492					sleepy = true;
493				} else if (recently_sleepy()) {
494					sleepy = true;
495				}
496			}
497			if (pv_sleepy_lock_sticky && seen_preempted &&
498			    !(val & _Q_SLEEPY_VAL)) {
499				if (try_set_sleepy(lock, val))
500					val |= _Q_SLEEPY_VAL;
501			}
502		}
503
504		if (preempted) {
505			seen_preempted = true;
506			sleepy = true;
507			if (!pv_spin_on_preempted_owner)
508				iters++;
509			/*
510			 * pv_spin_on_preempted_owner don't increase iters
511			 * while the owner is preempted -- we won't interfere
512			 * with it by definition. This could introduce some
513			 * latency issue if we continually observe preempted
514			 * owners, but hopefully that's a rare corner case of
515			 * a badly oversubscribed system.
516			 */
517		} else {
518			iters++;
519		}
520	} while (!steal_break(val, iters, paravirt, sleepy));
521
522	spin_end();
523
524	return false;
525}
526
527static __always_inline void queued_spin_lock_mcs_queue(struct qspinlock *lock, bool paravirt)
528{
529	struct qnodes *qnodesp;
530	struct qnode *next, *node;
531	u32 val, old, tail;
532	bool seen_preempted = false;
533	bool sleepy = false;
534	bool mustq = false;
535	int idx;
536	int iters = 0;
537
538	BUILD_BUG_ON(CONFIG_NR_CPUS >= (1U << _Q_TAIL_CPU_BITS));
539
540	qnodesp = this_cpu_ptr(&qnodes);
541	if (unlikely(qnodesp->count >= MAX_NODES)) {
542		spec_barrier();
543		while (!queued_spin_trylock(lock))
544			cpu_relax();
545		return;
546	}
547
548	idx = qnodesp->count++;
549	/*
550	 * Ensure that we increment the head node->count before initialising
551	 * the actual node. If the compiler is kind enough to reorder these
552	 * stores, then an IRQ could overwrite our assignments.
553	 */
554	barrier();
555	node = &qnodesp->nodes[idx];
556	node->next = NULL;
557	node->lock = lock;
558	node->cpu = smp_processor_id();
559	node->sleepy = 0;
560	node->locked = 0;
561
562	tail = encode_tail_cpu(node->cpu);
563
564	/*
565	 * Assign all attributes of a node before it can be published.
566	 * Issues an lwsync, serving as a release barrier, as well as a
567	 * compiler barrier.
568	 */
569	old = publish_tail_cpu(lock, tail);
570
571	/*
572	 * If there was a previous node; link it and wait until reaching the
573	 * head of the waitqueue.
574	 */
575	if (old & _Q_TAIL_CPU_MASK) {
576		int prev_cpu = decode_tail_cpu(old);
577		struct qnode *prev = get_tail_qnode(lock, prev_cpu);
578
579		/* Link @node into the waitqueue. */
580		WRITE_ONCE(prev->next, node);
581
582		/* Wait for mcs node lock to be released */
583		spin_begin();
584		while (!READ_ONCE(node->locked)) {
585			spec_barrier();
586
587			if (yield_to_prev(lock, node, prev_cpu, paravirt))
588				seen_preempted = true;
589		}
590		spec_barrier();
591		spin_end();
592
593		smp_rmb(); /* acquire barrier for the mcs lock */
594
595		/*
596		 * Generic qspinlocks have this prefetch here, but it seems
597		 * like it could cause additional line transitions because
598		 * the waiter will keep loading from it.
599		 */
600		if (_Q_SPIN_PREFETCH_NEXT) {
601			next = READ_ONCE(node->next);
602			if (next)
603				prefetchw(next);
604		}
605	}
606
607	/* We're at the head of the waitqueue, wait for the lock. */
608again:
609	spin_begin();
610	for (;;) {
611		bool preempted;
612
613		val = READ_ONCE(lock->val);
614		if (!(val & _Q_LOCKED_VAL))
615			break;
616		spec_barrier();
617
618		if (paravirt && pv_sleepy_lock && maybe_stealers) {
619			if (!sleepy) {
620				if (val & _Q_SLEEPY_VAL) {
621					seen_sleepy_lock();
622					sleepy = true;
623				} else if (recently_sleepy()) {
624					sleepy = true;
625				}
626			}
627			if (pv_sleepy_lock_sticky && seen_preempted &&
628			    !(val & _Q_SLEEPY_VAL)) {
629				if (try_set_sleepy(lock, val))
630					val |= _Q_SLEEPY_VAL;
631			}
632		}
633
634		propagate_sleepy(node, val, paravirt);
635		preempted = yield_head_to_locked_owner(lock, val, paravirt);
636		if (!maybe_stealers)
637			continue;
638
639		if (preempted)
640			seen_preempted = true;
641
642		if (paravirt && preempted) {
643			sleepy = true;
644
645			if (!pv_spin_on_preempted_owner)
646				iters++;
647		} else {
648			iters++;
649		}
650
651		if (!mustq && iters >= get_head_spins(paravirt, sleepy)) {
652			mustq = true;
653			set_mustq(lock);
654			val |= _Q_MUST_Q_VAL;
655		}
656	}
657	spec_barrier();
658	spin_end();
659
660	/* If we're the last queued, must clean up the tail. */
661	old = trylock_clean_tail(lock, tail);
662	if (unlikely(old & _Q_LOCKED_VAL)) {
663		BUG_ON(!maybe_stealers);
664		goto again; /* Can only be true if maybe_stealers. */
665	}
666
667	if ((old & _Q_TAIL_CPU_MASK) == tail)
668		goto release; /* We were the tail, no next. */
669
670	/* There is a next, must wait for node->next != NULL (MCS protocol) */
671	next = READ_ONCE(node->next);
672	if (!next) {
673		spin_begin();
674		while (!(next = READ_ONCE(node->next)))
675			cpu_relax();
676		spin_end();
677	}
678	spec_barrier();
679
680	/*
681	 * Unlock the next mcs waiter node. Release barrier is not required
682	 * here because the acquirer is only accessing the lock word, and
683	 * the acquire barrier we took the lock with orders that update vs
684	 * this store to locked. The corresponding barrier is the smp_rmb()
685	 * acquire barrier for mcs lock, above.
686	 */
687	if (paravirt && pv_prod_head) {
688		int next_cpu = next->cpu;
689		WRITE_ONCE(next->locked, 1);
690		if (_Q_SPIN_MISO)
691			asm volatile("miso" ::: "memory");
692		if (vcpu_is_preempted(next_cpu))
693			prod_cpu(next_cpu);
694	} else {
695		WRITE_ONCE(next->locked, 1);
696		if (_Q_SPIN_MISO)
697			asm volatile("miso" ::: "memory");
698	}
699
700release:
701	/*
702	 * Clear the lock before releasing the node, as another CPU might see stale
703	 * values if an interrupt occurs after we increment qnodesp->count
704	 * but before node->lock is initialized. The barrier ensures that
705	 * there are no further stores to the node after it has been released.
706	 */
707	node->lock = NULL;
708	barrier();
709	qnodesp->count--;
710}
711
712void __lockfunc queued_spin_lock_slowpath(struct qspinlock *lock)
713{
714	trace_contention_begin(lock, LCB_F_SPIN);
715	/*
716	 * This looks funny, but it induces the compiler to inline both
717	 * sides of the branch rather than share code as when the condition
718	 * is passed as the paravirt argument to the functions.
719	 */
720	if (IS_ENABLED(CONFIG_PARAVIRT_SPINLOCKS) && is_shared_processor()) {
721		if (try_to_steal_lock(lock, true))
722			spec_barrier();
723		else
724			queued_spin_lock_mcs_queue(lock, true);
725	} else {
726		if (try_to_steal_lock(lock, false))
727			spec_barrier();
728		else
729			queued_spin_lock_mcs_queue(lock, false);
730	}
731	trace_contention_end(lock, 0);
732}
733EXPORT_SYMBOL(queued_spin_lock_slowpath);
734
735#ifdef CONFIG_PARAVIRT_SPINLOCKS
736void pv_spinlocks_init(void)
737{
738}
739#endif
740
741#include <linux/debugfs.h>
742static int steal_spins_set(void *data, u64 val)
743{
744#if _Q_SPIN_TRY_LOCK_STEAL == 1
745	/* MAYBE_STEAL remains true */
746	steal_spins = val;
747#else
748	static DEFINE_MUTEX(lock);
749
750	/*
751	 * The lock slow path has a !maybe_stealers case that can assume
752	 * the head of queue will not see concurrent waiters. That waiter
753	 * is unsafe in the presence of stealers, so must keep them away
754	 * from one another.
755	 */
756
757	mutex_lock(&lock);
758	if (val && !steal_spins) {
759		maybe_stealers = true;
760		/* wait for queue head waiter to go away */
761		synchronize_rcu();
762		steal_spins = val;
763	} else if (!val && steal_spins) {
764		steal_spins = val;
765		/* wait for all possible stealers to go away */
766		synchronize_rcu();
767		maybe_stealers = false;
768	} else {
769		steal_spins = val;
770	}
771	mutex_unlock(&lock);
772#endif
773
774	return 0;
775}
776
777static int steal_spins_get(void *data, u64 *val)
778{
779	*val = steal_spins;
780
781	return 0;
782}
783
784DEFINE_SIMPLE_ATTRIBUTE(fops_steal_spins, steal_spins_get, steal_spins_set, "%llu\n");
785
786static int remote_steal_spins_set(void *data, u64 val)
787{
788	remote_steal_spins = val;
789
790	return 0;
791}
792
793static int remote_steal_spins_get(void *data, u64 *val)
794{
795	*val = remote_steal_spins;
796
797	return 0;
798}
799
800DEFINE_SIMPLE_ATTRIBUTE(fops_remote_steal_spins, remote_steal_spins_get, remote_steal_spins_set, "%llu\n");
801
802static int head_spins_set(void *data, u64 val)
803{
804	head_spins = val;
805
806	return 0;
807}
808
809static int head_spins_get(void *data, u64 *val)
810{
811	*val = head_spins;
812
813	return 0;
814}
815
816DEFINE_SIMPLE_ATTRIBUTE(fops_head_spins, head_spins_get, head_spins_set, "%llu\n");
817
818static int pv_yield_owner_set(void *data, u64 val)
819{
820	pv_yield_owner = !!val;
821
822	return 0;
823}
824
825static int pv_yield_owner_get(void *data, u64 *val)
826{
827	*val = pv_yield_owner;
828
829	return 0;
830}
831
832DEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_owner, pv_yield_owner_get, pv_yield_owner_set, "%llu\n");
833
834static int pv_yield_allow_steal_set(void *data, u64 val)
835{
836	pv_yield_allow_steal = !!val;
837
838	return 0;
839}
840
841static int pv_yield_allow_steal_get(void *data, u64 *val)
842{
843	*val = pv_yield_allow_steal;
844
845	return 0;
846}
847
848DEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_allow_steal, pv_yield_allow_steal_get, pv_yield_allow_steal_set, "%llu\n");
849
850static int pv_spin_on_preempted_owner_set(void *data, u64 val)
851{
852	pv_spin_on_preempted_owner = !!val;
853
854	return 0;
855}
856
857static int pv_spin_on_preempted_owner_get(void *data, u64 *val)
858{
859	*val = pv_spin_on_preempted_owner;
860
861	return 0;
862}
863
864DEFINE_SIMPLE_ATTRIBUTE(fops_pv_spin_on_preempted_owner, pv_spin_on_preempted_owner_get, pv_spin_on_preempted_owner_set, "%llu\n");
865
866static int pv_sleepy_lock_set(void *data, u64 val)
867{
868	pv_sleepy_lock = !!val;
869
870	return 0;
871}
872
873static int pv_sleepy_lock_get(void *data, u64 *val)
874{
875	*val = pv_sleepy_lock;
876
877	return 0;
878}
879
880DEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock, pv_sleepy_lock_get, pv_sleepy_lock_set, "%llu\n");
881
882static int pv_sleepy_lock_sticky_set(void *data, u64 val)
883{
884	pv_sleepy_lock_sticky = !!val;
885
886	return 0;
887}
888
889static int pv_sleepy_lock_sticky_get(void *data, u64 *val)
890{
891	*val = pv_sleepy_lock_sticky;
892
893	return 0;
894}
895
896DEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock_sticky, pv_sleepy_lock_sticky_get, pv_sleepy_lock_sticky_set, "%llu\n");
897
898static int pv_sleepy_lock_interval_ns_set(void *data, u64 val)
899{
900	pv_sleepy_lock_interval_ns = val;
901
902	return 0;
903}
904
905static int pv_sleepy_lock_interval_ns_get(void *data, u64 *val)
906{
907	*val = pv_sleepy_lock_interval_ns;
908
909	return 0;
910}
911
912DEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock_interval_ns, pv_sleepy_lock_interval_ns_get, pv_sleepy_lock_interval_ns_set, "%llu\n");
913
914static int pv_sleepy_lock_factor_set(void *data, u64 val)
915{
916	pv_sleepy_lock_factor = val;
917
918	return 0;
919}
920
921static int pv_sleepy_lock_factor_get(void *data, u64 *val)
922{
923	*val = pv_sleepy_lock_factor;
924
925	return 0;
926}
927
928DEFINE_SIMPLE_ATTRIBUTE(fops_pv_sleepy_lock_factor, pv_sleepy_lock_factor_get, pv_sleepy_lock_factor_set, "%llu\n");
929
930static int pv_yield_prev_set(void *data, u64 val)
931{
932	pv_yield_prev = !!val;
933
934	return 0;
935}
936
937static int pv_yield_prev_get(void *data, u64 *val)
938{
939	*val = pv_yield_prev;
940
941	return 0;
942}
943
944DEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_prev, pv_yield_prev_get, pv_yield_prev_set, "%llu\n");
945
946static int pv_yield_sleepy_owner_set(void *data, u64 val)
947{
948	pv_yield_sleepy_owner = !!val;
949
950	return 0;
951}
952
953static int pv_yield_sleepy_owner_get(void *data, u64 *val)
954{
955	*val = pv_yield_sleepy_owner;
956
957	return 0;
958}
959
960DEFINE_SIMPLE_ATTRIBUTE(fops_pv_yield_sleepy_owner, pv_yield_sleepy_owner_get, pv_yield_sleepy_owner_set, "%llu\n");
961
962static int pv_prod_head_set(void *data, u64 val)
963{
964	pv_prod_head = !!val;
965
966	return 0;
967}
968
969static int pv_prod_head_get(void *data, u64 *val)
970{
971	*val = pv_prod_head;
972
973	return 0;
974}
975
976DEFINE_SIMPLE_ATTRIBUTE(fops_pv_prod_head, pv_prod_head_get, pv_prod_head_set, "%llu\n");
977
978static __init int spinlock_debugfs_init(void)
979{
980	debugfs_create_file("qspl_steal_spins", 0600, arch_debugfs_dir, NULL, &fops_steal_spins);
981	debugfs_create_file("qspl_remote_steal_spins", 0600, arch_debugfs_dir, NULL, &fops_remote_steal_spins);
982	debugfs_create_file("qspl_head_spins", 0600, arch_debugfs_dir, NULL, &fops_head_spins);
983	if (is_shared_processor()) {
984		debugfs_create_file("qspl_pv_yield_owner", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_owner);
985		debugfs_create_file("qspl_pv_yield_allow_steal", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_allow_steal);
986		debugfs_create_file("qspl_pv_spin_on_preempted_owner", 0600, arch_debugfs_dir, NULL, &fops_pv_spin_on_preempted_owner);
987		debugfs_create_file("qspl_pv_sleepy_lock", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock);
988		debugfs_create_file("qspl_pv_sleepy_lock_sticky", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock_sticky);
989		debugfs_create_file("qspl_pv_sleepy_lock_interval_ns", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock_interval_ns);
990		debugfs_create_file("qspl_pv_sleepy_lock_factor", 0600, arch_debugfs_dir, NULL, &fops_pv_sleepy_lock_factor);
991		debugfs_create_file("qspl_pv_yield_prev", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_prev);
992		debugfs_create_file("qspl_pv_yield_sleepy_owner", 0600, arch_debugfs_dir, NULL, &fops_pv_yield_sleepy_owner);
993		debugfs_create_file("qspl_pv_prod_head", 0600, arch_debugfs_dir, NULL, &fops_pv_prod_head);
994	}
995
996	return 0;
997}
998device_initcall(spinlock_debugfs_init);
~/ Read-only file preview.