18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Hyper-V specific spinlock code.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2018, Intel, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author : Yi Sun <yi.y.sun@intel.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "Hyper-V: " fmt
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/mshyperv.h>
168c2ecf20Sopenharmony_ci#include <asm/paravirt.h>
178c2ecf20Sopenharmony_ci#include <asm/apic.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic bool __initdata hv_pvspin = true;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void hv_qlock_kick(int cpu)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	apic->send_IPI(cpu, X86_PLATFORM_IPI_VECTOR);
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic void hv_qlock_wait(u8 *byte, u8 val)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	unsigned long msr_val;
298c2ecf20Sopenharmony_ci	unsigned long flags;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (in_nmi())
328c2ecf20Sopenharmony_ci		return;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	/*
358c2ecf20Sopenharmony_ci	 * Reading HV_X64_MSR_GUEST_IDLE MSR tells the hypervisor that the
368c2ecf20Sopenharmony_ci	 * vCPU can be put into 'idle' state. This 'idle' state is
378c2ecf20Sopenharmony_ci	 * terminated by an IPI, usually from hv_qlock_kick(), even if
388c2ecf20Sopenharmony_ci	 * interrupts are disabled on the vCPU.
398c2ecf20Sopenharmony_ci	 *
408c2ecf20Sopenharmony_ci	 * To prevent a race against the unlock path it is required to
418c2ecf20Sopenharmony_ci	 * disable interrupts before accessing the HV_X64_MSR_GUEST_IDLE
428c2ecf20Sopenharmony_ci	 * MSR. Otherwise, if the IPI from hv_qlock_kick() arrives between
438c2ecf20Sopenharmony_ci	 * the lock value check and the rdmsrl() then the vCPU might be put
448c2ecf20Sopenharmony_ci	 * into 'idle' state by the hypervisor and kept in that state for
458c2ecf20Sopenharmony_ci	 * an unspecified amount of time.
468c2ecf20Sopenharmony_ci	 */
478c2ecf20Sopenharmony_ci	local_irq_save(flags);
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * Only issue the rdmsrl() when the lock state has not changed.
508c2ecf20Sopenharmony_ci	 */
518c2ecf20Sopenharmony_ci	if (READ_ONCE(*byte) == val)
528c2ecf20Sopenharmony_ci		rdmsrl(HV_X64_MSR_GUEST_IDLE, msr_val);
538c2ecf20Sopenharmony_ci	local_irq_restore(flags);
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Hyper-V does not support this so far.
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci__visible bool hv_vcpu_is_preempted(int vcpu)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	return false;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ciPV_CALLEE_SAVE_REGS_THUNK(hv_vcpu_is_preempted);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_civoid __init hv_init_spinlocks(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	if (!hv_pvspin || !apic ||
688c2ecf20Sopenharmony_ci	    !(ms_hyperv.hints & HV_X64_CLUSTER_IPI_RECOMMENDED) ||
698c2ecf20Sopenharmony_ci	    !(ms_hyperv.features & HV_MSR_GUEST_IDLE_AVAILABLE)) {
708c2ecf20Sopenharmony_ci		pr_info("PV spinlocks disabled\n");
718c2ecf20Sopenharmony_ci		return;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	pr_info("PV spinlocks enabled\n");
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	__pv_init_lock_hash();
768c2ecf20Sopenharmony_ci	pv_ops.lock.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath;
778c2ecf20Sopenharmony_ci	pv_ops.lock.queued_spin_unlock = PV_CALLEE_SAVE(__pv_queued_spin_unlock);
788c2ecf20Sopenharmony_ci	pv_ops.lock.wait = hv_qlock_wait;
798c2ecf20Sopenharmony_ci	pv_ops.lock.kick = hv_qlock_kick;
808c2ecf20Sopenharmony_ci	pv_ops.lock.vcpu_is_preempted = PV_CALLEE_SAVE(hv_vcpu_is_preempted);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic __init int hv_parse_nopvspin(char *arg)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	hv_pvspin = false;
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ciearly_param("hv_nopvspin", hv_parse_nopvspin);
89