Commit 858bccb7 authored by Zengruan Ye's avatar Zengruan Ye Committed by lishusen
Browse files

KVM: arm64: Support the vCPU preemption check

virt inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8WMFU


CVE: NA

--------------------------------

Support the vcpu_is_preempted() functionality under KVM/arm64. This will
enhance lock performance on overcommitted hosts (more runnable vCPUs
than physical CPUs in the system) as doing busy waits for preempted
vCPUs will hurt system performance far worse than early yielding.

unix benchmark result:
  host:  kernel 4.19.87, HiSilicon Kunpeng920, 8 CPUs
  guest: kernel 4.19.87, 16 vCPUs

               test-case                |    after-patch    |   before-patch
----------------------------------------+-------------------+------------------
 Dhrystone 2 using register variables   | 338955728.5 lps   | 339266319.5 lps
 Double-Precision Whetstone             |     30634.9 MWIPS |     30884.4 MWIPS
 Execl Throughput                       |      6753.2 lps   |      3580.1 lps
 File Copy 1024 bufsize 2000 maxblocks  |    490048.0 KBps  |    313282.3 KBps
 File Copy 256 bufsize 500 maxblocks    |    129662.5 KBps  |     83550.7 KBps
 File Copy 4096 bufsize 8000 maxblocks  |   1552551.5 KBps  |    814327.0 KBps
 Pipe Throughput                        |   8976422.5 lps   |   9048628.4 lps
 Pipe-based Context Switching           |    258641.7 lps   |    252925.9 lps
 Process Creation                       |      5312.2 lps   |      4507.9 lps
 Shell Scripts (1 concurrent)           |      8704.2 lpm   |      6720.9 lpm
 Shell Scripts (8 concurrent)           |      1708.8 lpm   |       607.2 lpm
 System Call Overhead                   |   3714444.7 lps   |   3746386.8 lps
----------------------------------------+-------------------+------------------
 System Benchmarks Index Score          |      2270.6       |      1679.2

Signed-off-by: default avatarZengruan Ye <yezengruan@huawei.com>
Signed-off-by: default avatarlishusen <lishusen2@huawei.com>
parent dd18a07f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -596,10 +596,12 @@ struct kvm_vcpu_arch {
		gpa_t base;
	} steal;

#ifdef CONFIG_PARAVIRT_SCHED
	/* Guest PV sched state */
	struct {
		gpa_t base;
	} pvsched;
#endif

	/* Per-vcpu CCSIDR override or NULL */
	u32 *ccsidr;
+5 −0
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@ static inline u64 paravirt_steal_clock(int cpu)

int __init pv_time_init(void);

#ifdef CONFIG_PARAVIRT_SCHED
int __init pv_sched_init(void);

__visible bool __native_vcpu_is_preempted(int cpu);
DECLARE_STATIC_CALL(pv_vcpu_preempted, __native_vcpu_is_preempted);

@@ -27,10 +30,12 @@ static inline bool pv_vcpu_is_preempted(int cpu)
{
	return static_call(pv_vcpu_preempted)(cpu);
}
#endif /* CONFIG_PARAVIRT_SCHED */

#else

#define pv_time_init() do {} while (0)
#define pv_sched_init() do {} while (0)

#endif // CONFIG_PARAVIRT

+2 −0
Original line number Diff line number Diff line
@@ -7,10 +7,12 @@
#ifndef __ASM_PVSCHED_ABI_H
#define __ASM_PVSCHED_ABI_H

#ifdef CONFIG_PARAVIRT_SCHED
struct pvsched_vcpu_state {
	__le32 preempted;
	/* Structure must be 64 byte aligned, pad to that size */
	u8 padding[60];
} __packed;
#endif /* CONFIG_PARAVIRT_SCHED */

#endif
+2 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
 * Author: Zengruan Ye <yezengruan@huawei.com>
 */

#ifdef CONFIG_PARAVIRT_SCHED
#include <linux/static_call.h>
#include <linux/spinlock.h>
#include <asm/paravirt.h>
@@ -14,3 +15,4 @@ __visible bool __native_vcpu_is_preempted(int cpu)
}

DEFINE_STATIC_CALL(pv_vcpu_preempted, __native_vcpu_is_preempted);
#endif /* CONFIG_PARAVIRT_SCHED */
+108 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#include <asm/paravirt.h>
#include <asm/pvclock-abi.h>
#include <asm/pvsched-abi.h>
#include <asm/smp_plat.h>

struct static_key paravirt_steal_enabled;
@@ -174,3 +175,110 @@ int __init pv_time_init(void)

	return 0;
}

#ifdef CONFIG_PARAVIRT_SCHED
DEFINE_PER_CPU(struct pvsched_vcpu_state, pvsched_vcpu_region) __aligned(64);
EXPORT_PER_CPU_SYMBOL(pvsched_vcpu_region);

static bool kvm_vcpu_is_preempted(int cpu)
{
	struct pvsched_vcpu_state *reg;
	u32 preempted;

	reg = &per_cpu(pvsched_vcpu_region, cpu);
	if (!reg) {
		pr_warn_once("PV sched enabled but not configured for cpu %d\n",
			     cpu);
		return false;
	}

	preempted = le32_to_cpu(READ_ONCE(reg->preempted));

	return !!preempted;
}

static int pvsched_vcpu_state_dying_cpu(unsigned int cpu)
{
	struct pvsched_vcpu_state *reg;
	struct arm_smccc_res res;

	reg = this_cpu_ptr(&pvsched_vcpu_region);
	if (!reg)
		return -EFAULT;

	arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_SCHED_IPA_RELEASE, &res);
	memset(reg, 0, sizeof(*reg));

	return 0;
}

static int init_pvsched_vcpu_state(unsigned int cpu)
{
	struct pvsched_vcpu_state *reg;
	struct arm_smccc_res res;

	reg = this_cpu_ptr(&pvsched_vcpu_region);
	if (!reg)
		return -EFAULT;

	/* Pass the memory address to host via hypercall */
	arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_SCHED_IPA_INIT,
			     virt_to_phys(reg), &res);

	return 0;
}

static int kvm_arm_init_pvsched(void)
{
	int ret;

	ret = cpuhp_setup_state(CPUHP_AP_ARM_KVM_PVSCHED_STARTING,
				"hypervisor/arm/pvsched:starting",
				init_pvsched_vcpu_state,
				pvsched_vcpu_state_dying_cpu);

	if (ret < 0) {
		pr_warn("PV sched init failed\n");
		return ret;
	}

	return 0;
}

static bool has_kvm_pvsched(void)
{
	struct arm_smccc_res res;

	/* To detect the presence of PV sched support we require SMCCC 1.1+ */
	if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
		return false;

	arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
			     ARM_SMCCC_HV_PV_SCHED_FEATURES, &res);

	return (res.a0 == SMCCC_RET_SUCCESS);
}

int __init pv_sched_init(void)
{
	int ret;

	if (is_hyp_mode_available())
		return 0;

	if (!has_kvm_pvsched()) {
		pr_warn("PV sched is not available\n");
		return 0;
	}

	ret = kvm_arm_init_pvsched();
	if (ret)
		return ret;

	static_call_update(pv_vcpu_preempted, kvm_vcpu_is_preempted);
	pr_info("using PV sched preempted\n");

	return 0;
}
early_initcall(pv_sched_init);
#endif /* CONFIG_PARAVIRT_SCHED */
Loading