162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Split spinlock implementation out into its own file, so it can be 462306a36Sopenharmony_ci * compiled in a FTRACE-compatible way. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/spinlock.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/atomic.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/paravirt.h> 1262306a36Sopenharmony_ci#include <asm/qspinlock.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <xen/events.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "xen-ops.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic DEFINE_PER_CPU(int, lock_kicker_irq) = -1; 1962306a36Sopenharmony_cistatic DEFINE_PER_CPU(char *, irq_name); 2062306a36Sopenharmony_cistatic DEFINE_PER_CPU(atomic_t, xen_qlock_wait_nest); 2162306a36Sopenharmony_cistatic bool xen_pvspin = true; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void xen_qlock_kick(int cpu) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci int irq = per_cpu(lock_kicker_irq, cpu); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* Don't kick if the target's kicker interrupt is not initialized. */ 2862306a36Sopenharmony_ci if (irq == -1) 2962306a36Sopenharmony_ci return; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); 3262306a36Sopenharmony_ci} 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Halt the current CPU & release it back to the host 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic void xen_qlock_wait(u8 *byte, u8 val) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci int irq = __this_cpu_read(lock_kicker_irq); 4062306a36Sopenharmony_ci atomic_t *nest_cnt = this_cpu_ptr(&xen_qlock_wait_nest); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* If kicker interrupts not initialized yet, just spin */ 4362306a36Sopenharmony_ci if (irq == -1 || in_nmi()) 4462306a36Sopenharmony_ci return; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* Detect reentry. */ 4762306a36Sopenharmony_ci atomic_inc(nest_cnt); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci /* If irq pending already and no nested call clear it. */ 5062306a36Sopenharmony_ci if (atomic_read(nest_cnt) == 1 && xen_test_irq_pending(irq)) { 5162306a36Sopenharmony_ci xen_clear_irq_pending(irq); 5262306a36Sopenharmony_ci } else if (READ_ONCE(*byte) == val) { 5362306a36Sopenharmony_ci /* Block until irq becomes pending (or a spurious wakeup) */ 5462306a36Sopenharmony_ci xen_poll_irq(irq); 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci atomic_dec(nest_cnt); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic irqreturn_t dummy_handler(int irq, void *dev_id) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci BUG(); 6362306a36Sopenharmony_ci return IRQ_HANDLED; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_civoid xen_init_lock_cpu(int cpu) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int irq; 6962306a36Sopenharmony_ci char *name; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (!xen_pvspin) 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci WARN(per_cpu(lock_kicker_irq, cpu) >= 0, "spinlock on CPU%d exists on IRQ%d!\n", 7562306a36Sopenharmony_ci cpu, per_cpu(lock_kicker_irq, cpu)); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); 7862306a36Sopenharmony_ci per_cpu(irq_name, cpu) = name; 7962306a36Sopenharmony_ci irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, 8062306a36Sopenharmony_ci cpu, 8162306a36Sopenharmony_ci dummy_handler, 8262306a36Sopenharmony_ci IRQF_PERCPU|IRQF_NOBALANCING, 8362306a36Sopenharmony_ci name, 8462306a36Sopenharmony_ci NULL); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (irq >= 0) { 8762306a36Sopenharmony_ci disable_irq(irq); /* make sure it's never delivered */ 8862306a36Sopenharmony_ci per_cpu(lock_kicker_irq, cpu) = irq; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci printk("cpu %d spinlock event irq %d\n", cpu, irq); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_civoid xen_uninit_lock_cpu(int cpu) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci int irq; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!xen_pvspin) 9962306a36Sopenharmony_ci return; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci kfree(per_cpu(irq_name, cpu)); 10262306a36Sopenharmony_ci per_cpu(irq_name, cpu) = NULL; 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * When booting the kernel with 'mitigations=auto,nosmt', the secondary 10562306a36Sopenharmony_ci * CPUs are not activated, and lock_kicker_irq is not initialized. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci irq = per_cpu(lock_kicker_irq, cpu); 10862306a36Sopenharmony_ci if (irq == -1) 10962306a36Sopenharmony_ci return; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci unbind_from_irqhandler(irq, NULL); 11262306a36Sopenharmony_ci per_cpu(lock_kicker_irq, cpu) = -1; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciPV_CALLEE_SAVE_REGS_THUNK(xen_vcpu_stolen); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* 11862306a36Sopenharmony_ci * Our init of PV spinlocks is split in two init functions due to us 11962306a36Sopenharmony_ci * using paravirt patching and jump labels patching and having to do 12062306a36Sopenharmony_ci * all of this before SMP code is invoked. 12162306a36Sopenharmony_ci * 12262306a36Sopenharmony_ci * The paravirt patching needs to be done _before_ the alternative asm code 12362306a36Sopenharmony_ci * is started, otherwise we would not patch the core kernel code. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_civoid __init xen_init_spinlocks(void) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci /* Don't need to use pvqspinlock code if there is only 1 vCPU. */ 12862306a36Sopenharmony_ci if (num_possible_cpus() == 1 || nopvspin) 12962306a36Sopenharmony_ci xen_pvspin = false; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!xen_pvspin) { 13262306a36Sopenharmony_ci printk(KERN_DEBUG "xen: PV spinlocks disabled\n"); 13362306a36Sopenharmony_ci static_branch_disable(&virt_spin_lock_key); 13462306a36Sopenharmony_ci return; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci printk(KERN_DEBUG "xen: PV spinlocks enabled\n"); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci __pv_init_lock_hash(); 13962306a36Sopenharmony_ci pv_ops.lock.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath; 14062306a36Sopenharmony_ci pv_ops.lock.queued_spin_unlock = 14162306a36Sopenharmony_ci PV_CALLEE_SAVE(__pv_queued_spin_unlock); 14262306a36Sopenharmony_ci pv_ops.lock.wait = xen_qlock_wait; 14362306a36Sopenharmony_ci pv_ops.lock.kick = xen_qlock_kick; 14462306a36Sopenharmony_ci pv_ops.lock.vcpu_is_preempted = PV_CALLEE_SAVE(xen_vcpu_stolen); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic __init int xen_parse_nopvspin(char *arg) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci pr_notice("\"xen_nopvspin\" is deprecated, please use \"nopvspin\" instead\n"); 15062306a36Sopenharmony_ci xen_pvspin = false; 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ciearly_param("xen_nopvspin", xen_parse_nopvspin); 15462306a36Sopenharmony_ci 155