diff options
author | Yi Sun <yi.y.sun@linux.intel.com> | 2018-10-08 04:29:34 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2018-10-09 08:21:39 -0400 |
commit | 3a025de64bf89c84a79909069e3c24ad9e710d27 (patch) | |
tree | 04adc9cf615d5644da69374dbf9d70b4a33cb63d | |
parent | f726c4620df39055f060537a8ed183c18a2c504b (diff) |
x86/hyperv: Enable PV qspinlock for Hyper-V
Implement the required wait and kick callbacks to support PV spinlocks in
Hyper-V guests.
[ tglx: Document the requirement for disabling interrupts in the wait()
callback. Remove goto and unnecessary includes. Add prototype
for hv_vcpu_is_preempted(). Adapted to pending paravirt changes. ]
Signed-off-by: Yi Sun <yi.y.sun@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Juergen Gross <jgross@suse.com>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: Stephen Hemminger <sthemmin@microsoft.com>
Cc: Michael Kelley (EOSG) <Michael.H.Kelley@microsoft.com>
Cc: chao.p.peng@intel.com
Cc: chao.gao@intel.com
Cc: isaku.yamahata@intel.com
Cc: tianyu.lan@microsoft.com
Link: https://lkml.kernel.org/r/1538987374-51217-3-git-send-email-yi.y.sun@linux.intel.com
-rw-r--r-- | Documentation/admin-guide/kernel-parameters.txt | 5 | ||||
-rw-r--r-- | arch/x86/hyperv/Makefile | 4 | ||||
-rw-r--r-- | arch/x86/hyperv/hv_spinlock.c | 88 | ||||
-rw-r--r-- | arch/x86/include/asm/mshyperv.h | 2 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mshyperv.c | 14 |
5 files changed, 113 insertions, 0 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 9871e649ffef..4cdf6a673592 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt | |||
@@ -1385,6 +1385,11 @@ | |||
1385 | hvc_iucv_allow= [S390] Comma-separated list of z/VM user IDs. | 1385 | hvc_iucv_allow= [S390] Comma-separated list of z/VM user IDs. |
1386 | If specified, z/VM IUCV HVC accepts connections | 1386 | If specified, z/VM IUCV HVC accepts connections |
1387 | from listed z/VM user IDs only. | 1387 | from listed z/VM user IDs only. |
1388 | |||
1389 | hv_nopvspin [X86,HYPER_V] Disables the paravirt spinlock optimizations | ||
1390 | which allow the hypervisor to 'idle' the | ||
1391 | guest on lock contention. | ||
1392 | |||
1388 | keep_bootcon [KNL] | 1393 | keep_bootcon [KNL] |
1389 | Do not unregister boot console at start. This is only | 1394 | Do not unregister boot console at start. This is only |
1390 | useful for debugging when something happens in the window | 1395 | useful for debugging when something happens in the window |
diff --git a/arch/x86/hyperv/Makefile b/arch/x86/hyperv/Makefile index b21ee65c4101..1c11f9420a82 100644 --- a/arch/x86/hyperv/Makefile +++ b/arch/x86/hyperv/Makefile | |||
@@ -1,2 +1,6 @@ | |||
1 | obj-y := hv_init.o mmu.o nested.o | 1 | obj-y := hv_init.o mmu.o nested.o |
2 | obj-$(CONFIG_X86_64) += hv_apic.o | 2 | obj-$(CONFIG_X86_64) += hv_apic.o |
3 | |||
4 | ifdef CONFIG_X86_64 | ||
5 | obj-$(CONFIG_PARAVIRT_SPINLOCKS) += hv_spinlock.o | ||
6 | endif | ||
diff --git a/arch/x86/hyperv/hv_spinlock.c b/arch/x86/hyperv/hv_spinlock.c new file mode 100644 index 000000000000..a861b0456b1a --- /dev/null +++ b/arch/x86/hyperv/hv_spinlock.c | |||
@@ -0,0 +1,88 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | |||
3 | /* | ||
4 | * Hyper-V specific spinlock code. | ||
5 | * | ||
6 | * Copyright (C) 2018, Intel, Inc. | ||
7 | * | ||
8 | * Author : Yi Sun <yi.y.sun@intel.com> | ||
9 | */ | ||
10 | |||
11 | #define pr_fmt(fmt) "Hyper-V: " fmt | ||
12 | |||
13 | #include <linux/spinlock.h> | ||
14 | |||
15 | #include <asm/mshyperv.h> | ||
16 | #include <asm/paravirt.h> | ||
17 | #include <asm/apic.h> | ||
18 | |||
19 | static bool __initdata hv_pvspin = true; | ||
20 | |||
21 | static void hv_qlock_kick(int cpu) | ||
22 | { | ||
23 | apic->send_IPI(cpu, X86_PLATFORM_IPI_VECTOR); | ||
24 | } | ||
25 | |||
26 | static void hv_qlock_wait(u8 *byte, u8 val) | ||
27 | { | ||
28 | unsigned long msr_val; | ||
29 | unsigned long flags; | ||
30 | |||
31 | if (in_nmi()) | ||
32 | return; | ||
33 | |||
34 | /* | ||
35 | * Reading HV_X64_MSR_GUEST_IDLE MSR tells the hypervisor that the | ||
36 | * vCPU can be put into 'idle' state. This 'idle' state is | ||
37 | * terminated by an IPI, usually from hv_qlock_kick(), even if | ||
38 | * interrupts are disabled on the vCPU. | ||
39 | * | ||
40 | * To prevent a race against the unlock path it is required to | ||
41 | * disable interrupts before accessing the HV_X64_MSR_GUEST_IDLE | ||
42 | * MSR. Otherwise, if the IPI from hv_qlock_kick() arrives between | ||
43 | * the lock value check and the rdmsrl() then the vCPU might be put | ||
44 | * into 'idle' state by the hypervisor and kept in that state for | ||
45 | * an unspecified amount of time. | ||
46 | */ | ||
47 | local_irq_save(flags); | ||
48 | /* | ||
49 | * Only issue the rdmsrl() when the lock state has not changed. | ||
50 | */ | ||
51 | if (READ_ONCE(*byte) == val) | ||
52 | rdmsrl(HV_X64_MSR_GUEST_IDLE, msr_val); | ||
53 | local_irq_restore(flags); | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Hyper-V does not support this so far. | ||
58 | */ | ||
59 | bool hv_vcpu_is_preempted(int vcpu) | ||
60 | { | ||
61 | return false; | ||
62 | } | ||
63 | PV_CALLEE_SAVE_REGS_THUNK(hv_vcpu_is_preempted); | ||
64 | |||
65 | void __init hv_init_spinlocks(void) | ||
66 | { | ||
67 | if (!hv_pvspin || !apic || | ||
68 | !(ms_hyperv.hints & HV_X64_CLUSTER_IPI_RECOMMENDED) || | ||
69 | !(ms_hyperv.features & HV_X64_MSR_GUEST_IDLE_AVAILABLE)) { | ||
70 | pr_info("PV spinlocks disabled\n"); | ||
71 | return; | ||
72 | } | ||
73 | pr_info("PV spinlocks enabled\n"); | ||
74 | |||
75 | __pv_init_lock_hash(); | ||
76 | pv_ops.lock.queued_spin_lock_slowpath = __pv_queued_spin_lock_slowpath; | ||
77 | pv_ops.lock.queued_spin_unlock = PV_CALLEE_SAVE(__pv_queued_spin_unlock); | ||
78 | pv_ops.lock.wait = hv_qlock_wait; | ||
79 | pv_ops.lock.kick = hv_qlock_kick; | ||
80 | pv_ops.lock.vcpu_is_preempted = PV_CALLEE_SAVE(hv_vcpu_is_preempted); | ||
81 | } | ||
82 | |||
83 | static __init int hv_parse_nopvspin(char *arg) | ||
84 | { | ||
85 | hv_pvspin = false; | ||
86 | return 0; | ||
87 | } | ||
88 | early_param("hv_nopvspin", hv_parse_nopvspin); | ||
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index f37704497d8f..0d6271cce198 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h | |||
@@ -351,6 +351,8 @@ int hyperv_flush_guest_mapping(u64 as); | |||
351 | 351 | ||
352 | #ifdef CONFIG_X86_64 | 352 | #ifdef CONFIG_X86_64 |
353 | void hv_apic_init(void); | 353 | void hv_apic_init(void); |
354 | void __init hv_init_spinlocks(void); | ||
355 | bool hv_vcpu_is_preempted(int vcpu); | ||
354 | #else | 356 | #else |
355 | static inline void hv_apic_init(void) {} | 357 | static inline void hv_apic_init(void) {} |
356 | #endif | 358 | #endif |
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index ad12733f6058..1c72f3819eb1 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c | |||
@@ -199,6 +199,16 @@ static unsigned long hv_get_tsc_khz(void) | |||
199 | return freq / 1000; | 199 | return freq / 1000; |
200 | } | 200 | } |
201 | 201 | ||
202 | #if defined(CONFIG_SMP) && IS_ENABLED(CONFIG_HYPERV) | ||
203 | static void __init hv_smp_prepare_boot_cpu(void) | ||
204 | { | ||
205 | native_smp_prepare_boot_cpu(); | ||
206 | #if defined(CONFIG_X86_64) && defined(CONFIG_PARAVIRT_SPINLOCKS) | ||
207 | hv_init_spinlocks(); | ||
208 | #endif | ||
209 | } | ||
210 | #endif | ||
211 | |||
202 | static void __init ms_hyperv_init_platform(void) | 212 | static void __init ms_hyperv_init_platform(void) |
203 | { | 213 | { |
204 | int hv_host_info_eax; | 214 | int hv_host_info_eax; |
@@ -303,6 +313,10 @@ static void __init ms_hyperv_init_platform(void) | |||
303 | if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE) | 313 | if (ms_hyperv.misc_features & HV_STIMER_DIRECT_MODE_AVAILABLE) |
304 | alloc_intr_gate(HYPERV_STIMER0_VECTOR, | 314 | alloc_intr_gate(HYPERV_STIMER0_VECTOR, |
305 | hv_stimer0_callback_vector); | 315 | hv_stimer0_callback_vector); |
316 | |||
317 | # ifdef CONFIG_SMP | ||
318 | smp_ops.smp_prepare_boot_cpu = hv_smp_prepare_boot_cpu; | ||
319 | # endif | ||
306 | #endif | 320 | #endif |
307 | } | 321 | } |
308 | 322 | ||