Merge branch 'x86-hyperv-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86/hyper-v update from Thomas Gleixner: "Add fast hypercall support for guest running on the Microsoft HyperV(isor)" * 'x86-hyperv-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/hyper-v: Fix wrong merge conflict resolution x86/hyper-v: Check for VP_INVAL in hyperv_flush_tlb_others() x86/hyper-v: Check cpumask_to_vpset() return value in hyperv_flush_tlb_others_ex() x86/hyper-v: Trace PV IPI send x86/hyper-v: Use cheaper HVCALL_SEND_IPI hypercall when possible x86/hyper-v: Use 'fast' hypercall for HVCALL_SEND_IPI x86/hyper-v: Implement hv_do_fast_hypercall16 x86/hyper-v: Use cheaper HVCALL_FLUSH_VIRTUAL_ADDRESS_{LIST,SPACE} hypercalls when possible
This commit is contained in:
commit
f499026456
|
@ -31,6 +31,8 @@
|
|||
#include <asm/mshyperv.h>
|
||||
#include <asm/apic.h>
|
||||
|
||||
#include <asm/trace/hyperv.h>
|
||||
|
||||
static struct apic orig_apic;
|
||||
|
||||
static u64 hv_apic_icr_read(void)
|
||||
|
@ -99,6 +101,9 @@ static bool __send_ipi_mask_ex(const struct cpumask *mask, int vector)
|
|||
int nr_bank = 0;
|
||||
int ret = 1;
|
||||
|
||||
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
|
||||
return false;
|
||||
|
||||
local_irq_save(flags);
|
||||
arg = (struct ipi_arg_ex **)this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||
|
||||
|
@ -130,10 +135,10 @@ ipi_mask_ex_done:
|
|||
static bool __send_ipi_mask(const struct cpumask *mask, int vector)
|
||||
{
|
||||
int cur_cpu, vcpu;
|
||||
struct ipi_arg_non_ex **arg;
|
||||
struct ipi_arg_non_ex *ipi_arg;
|
||||
struct ipi_arg_non_ex ipi_arg;
|
||||
int ret = 1;
|
||||
unsigned long flags;
|
||||
|
||||
trace_hyperv_send_ipi_mask(mask, vector);
|
||||
|
||||
if (cpumask_empty(mask))
|
||||
return true;
|
||||
|
@ -144,40 +149,43 @@ static bool __send_ipi_mask(const struct cpumask *mask, int vector)
|
|||
if ((vector < HV_IPI_LOW_VECTOR) || (vector > HV_IPI_HIGH_VECTOR))
|
||||
return false;
|
||||
|
||||
if ((ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
|
||||
return __send_ipi_mask_ex(mask, vector);
|
||||
/*
|
||||
* From the supplied CPU set we need to figure out if we can get away
|
||||
* with cheaper HVCALL_SEND_IPI hypercall. This is possible when the
|
||||
* highest VP number in the set is < 64. As VP numbers are usually in
|
||||
* ascending order and match Linux CPU ids, here is an optimization:
|
||||
* we check the VP number for the highest bit in the supplied set first
|
||||
* so we can quickly find out if using HVCALL_SEND_IPI_EX hypercall is
|
||||
* a must. We will also check all VP numbers when walking the supplied
|
||||
* CPU set to remain correct in all cases.
|
||||
*/
|
||||
if (hv_cpu_number_to_vp_number(cpumask_last(mask)) >= 64)
|
||||
goto do_ex_hypercall;
|
||||
|
||||
local_irq_save(flags);
|
||||
arg = (struct ipi_arg_non_ex **)this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||
|
||||
ipi_arg = *arg;
|
||||
if (unlikely(!ipi_arg))
|
||||
goto ipi_mask_done;
|
||||
|
||||
ipi_arg->vector = vector;
|
||||
ipi_arg->reserved = 0;
|
||||
ipi_arg->cpu_mask = 0;
|
||||
ipi_arg.vector = vector;
|
||||
ipi_arg.cpu_mask = 0;
|
||||
|
||||
for_each_cpu(cur_cpu, mask) {
|
||||
vcpu = hv_cpu_number_to_vp_number(cur_cpu);
|
||||
if (vcpu == VP_INVAL)
|
||||
goto ipi_mask_done;
|
||||
return false;
|
||||
|
||||
/*
|
||||
* This particular version of the IPI hypercall can
|
||||
* only target upto 64 CPUs.
|
||||
*/
|
||||
if (vcpu >= 64)
|
||||
goto ipi_mask_done;
|
||||
goto do_ex_hypercall;
|
||||
|
||||
__set_bit(vcpu, (unsigned long *)&ipi_arg->cpu_mask);
|
||||
__set_bit(vcpu, (unsigned long *)&ipi_arg.cpu_mask);
|
||||
}
|
||||
|
||||
ret = hv_do_hypercall(HVCALL_SEND_IPI, ipi_arg, NULL);
|
||||
|
||||
ipi_mask_done:
|
||||
local_irq_restore(flags);
|
||||
ret = hv_do_fast_hypercall16(HVCALL_SEND_IPI, ipi_arg.vector,
|
||||
ipi_arg.cpu_mask);
|
||||
return ((ret == 0) ? true : false);
|
||||
|
||||
do_ex_hypercall:
|
||||
return __send_ipi_mask_ex(mask, vector);
|
||||
}
|
||||
|
||||
static bool __send_ipi_one(int cpu, int vector)
|
||||
|
@ -233,10 +241,7 @@ static void hv_send_ipi_self(int vector)
|
|||
void __init hv_apic_init(void)
|
||||
{
|
||||
if (ms_hyperv.hints & HV_X64_CLUSTER_IPI_RECOMMENDED) {
|
||||
if ((ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
|
||||
pr_info("Hyper-V: Using ext hypercalls for IPI\n");
|
||||
else
|
||||
pr_info("Hyper-V: Using IPI hypercalls\n");
|
||||
pr_info("Hyper-V: Using IPI hypercalls\n");
|
||||
/*
|
||||
* Set the IPI entry points.
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
/* Each gva in gva_list encodes up to 4096 pages to flush */
|
||||
#define HV_TLB_FLUSH_UNIT (4096 * PAGE_SIZE)
|
||||
|
||||
static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
||||
const struct flush_tlb_info *info);
|
||||
|
||||
/*
|
||||
* Fills in gva_list starting from offset. Returns the number of items added.
|
||||
|
@ -93,10 +95,29 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
|
|||
if (cpumask_equal(cpus, cpu_present_mask)) {
|
||||
flush->flags |= HV_FLUSH_ALL_PROCESSORS;
|
||||
} else {
|
||||
/*
|
||||
* From the supplied CPU set we need to figure out if we can get
|
||||
* away with cheaper HVCALL_FLUSH_VIRTUAL_ADDRESS_{LIST,SPACE}
|
||||
* hypercalls. This is possible when the highest VP number in
|
||||
* the set is < 64. As VP numbers are usually in ascending order
|
||||
* and match Linux CPU ids, here is an optimization: we check
|
||||
* the VP number for the highest bit in the supplied set first
|
||||
* so we can quickly find out if using *_EX hypercalls is a
|
||||
* must. We will also check all VP numbers when walking the
|
||||
* supplied CPU set to remain correct in all cases.
|
||||
*/
|
||||
if (hv_cpu_number_to_vp_number(cpumask_last(cpus)) >= 64)
|
||||
goto do_ex_hypercall;
|
||||
|
||||
for_each_cpu(cpu, cpus) {
|
||||
vcpu = hv_cpu_number_to_vp_number(cpu);
|
||||
if (vcpu >= 64)
|
||||
if (vcpu == VP_INVAL) {
|
||||
local_irq_restore(flags);
|
||||
goto do_native;
|
||||
}
|
||||
|
||||
if (vcpu >= 64)
|
||||
goto do_ex_hypercall;
|
||||
|
||||
__set_bit(vcpu, (unsigned long *)
|
||||
&flush->processor_mask);
|
||||
|
@ -123,7 +144,12 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
|
|||
status = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST,
|
||||
gva_n, 0, flush, NULL);
|
||||
}
|
||||
goto check_status;
|
||||
|
||||
do_ex_hypercall:
|
||||
status = hyperv_flush_tlb_others_ex(cpus, info);
|
||||
|
||||
check_status:
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (!(status & HV_HYPERCALL_RESULT_MASK))
|
||||
|
@ -132,35 +158,22 @@ do_native:
|
|||
native_flush_tlb_others(cpus, info);
|
||||
}
|
||||
|
||||
static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
||||
const struct flush_tlb_info *info)
|
||||
static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
||||
const struct flush_tlb_info *info)
|
||||
{
|
||||
int nr_bank = 0, max_gvas, gva_n;
|
||||
struct hv_tlb_flush_ex **flush_pcpu;
|
||||
struct hv_tlb_flush_ex *flush;
|
||||
u64 status = U64_MAX;
|
||||
unsigned long flags;
|
||||
u64 status;
|
||||
|
||||
trace_hyperv_mmu_flush_tlb_others(cpus, info);
|
||||
|
||||
if (!hv_hypercall_pg)
|
||||
goto do_native;
|
||||
|
||||
if (cpumask_empty(cpus))
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
|
||||
return U64_MAX;
|
||||
|
||||
flush_pcpu = (struct hv_tlb_flush_ex **)
|
||||
this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||
|
||||
flush = *flush_pcpu;
|
||||
|
||||
if (unlikely(!flush)) {
|
||||
local_irq_restore(flags);
|
||||
goto do_native;
|
||||
}
|
||||
|
||||
if (info->mm) {
|
||||
/*
|
||||
* AddressSpace argument must match the CR3 with PCID bits
|
||||
|
@ -176,15 +189,10 @@ static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
|||
|
||||
flush->hv_vp_set.valid_bank_mask = 0;
|
||||
|
||||
if (!cpumask_equal(cpus, cpu_present_mask)) {
|
||||
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
||||
nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus);
|
||||
}
|
||||
|
||||
if (!nr_bank) {
|
||||
flush->hv_vp_set.format = HV_GENERIC_SET_ALL;
|
||||
flush->flags |= HV_FLUSH_ALL_PROCESSORS;
|
||||
}
|
||||
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
||||
nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus);
|
||||
if (nr_bank < 0)
|
||||
return U64_MAX;
|
||||
|
||||
/*
|
||||
* We can flush not more than max_gvas with one hypercall. Flush the
|
||||
|
@ -213,12 +221,7 @@ static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
|||
gva_n, nr_bank, flush, NULL);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (!(status & HV_HYPERCALL_RESULT_MASK))
|
||||
return;
|
||||
do_native:
|
||||
native_flush_tlb_others(cpus, info);
|
||||
return status;
|
||||
}
|
||||
|
||||
void hyperv_setup_mmu_ops(void)
|
||||
|
@ -226,11 +229,6 @@ void hyperv_setup_mmu_ops(void)
|
|||
if (!(ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED))
|
||||
return;
|
||||
|
||||
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) {
|
||||
pr_info("Using hypercall for remote TLB flush\n");
|
||||
pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others;
|
||||
} else {
|
||||
pr_info("Using ext hypercall for remote TLB flush\n");
|
||||
pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others_ex;
|
||||
}
|
||||
pr_info("Using hypercall for remote TLB flush\n");
|
||||
pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others;
|
||||
}
|
||||
|
|
|
@ -194,6 +194,40 @@ static inline u64 hv_do_fast_hypercall8(u16 code, u64 input1)
|
|||
return hv_status;
|
||||
}
|
||||
|
||||
/* Fast hypercall with 16 bytes of input */
|
||||
static inline u64 hv_do_fast_hypercall16(u16 code, u64 input1, u64 input2)
|
||||
{
|
||||
u64 hv_status, control = (u64)code | HV_HYPERCALL_FAST_BIT;
|
||||
|
||||
#ifdef CONFIG_X86_64
|
||||
{
|
||||
__asm__ __volatile__("mov %4, %%r8\n"
|
||||
CALL_NOSPEC
|
||||
: "=a" (hv_status), ASM_CALL_CONSTRAINT,
|
||||
"+c" (control), "+d" (input1)
|
||||
: "r" (input2),
|
||||
THUNK_TARGET(hv_hypercall_pg)
|
||||
: "cc", "r8", "r9", "r10", "r11");
|
||||
}
|
||||
#else
|
||||
{
|
||||
u32 input1_hi = upper_32_bits(input1);
|
||||
u32 input1_lo = lower_32_bits(input1);
|
||||
u32 input2_hi = upper_32_bits(input2);
|
||||
u32 input2_lo = lower_32_bits(input2);
|
||||
|
||||
__asm__ __volatile__ (CALL_NOSPEC
|
||||
: "=A"(hv_status),
|
||||
"+c"(input1_lo), ASM_CALL_CONSTRAINT
|
||||
: "A" (control), "b" (input1_hi),
|
||||
"D"(input2_hi), "S"(input2_lo),
|
||||
THUNK_TARGET(hv_hypercall_pg)
|
||||
: "cc");
|
||||
}
|
||||
#endif
|
||||
return hv_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rep hypercalls. Callers of this functions are supposed to ensure that
|
||||
* rep_count and varhead_size comply with Hyper-V hypercall definition.
|
||||
|
|
|
@ -28,6 +28,21 @@ TRACE_EVENT(hyperv_mmu_flush_tlb_others,
|
|||
__entry->addr, __entry->end)
|
||||
);
|
||||
|
||||
TRACE_EVENT(hyperv_send_ipi_mask,
|
||||
TP_PROTO(const struct cpumask *cpus,
|
||||
int vector),
|
||||
TP_ARGS(cpus, vector),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, ncpus)
|
||||
__field(int, vector)
|
||||
),
|
||||
TP_fast_assign(__entry->ncpus = cpumask_weight(cpus);
|
||||
__entry->vector = vector;
|
||||
),
|
||||
TP_printk("ncpus %d vector %x",
|
||||
__entry->ncpus, __entry->vector)
|
||||
);
|
||||
|
||||
#endif /* CONFIG_HYPERV */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
|
|
Loading…
Reference in New Issue