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