mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-30 11:56:43 +07:00
x86/hyper-v: Use cheaper HVCALL_FLUSH_VIRTUAL_ADDRESS_{LIST,SPACE} hypercalls when possible
While working on Hyper-V style PV TLB flush support in KVM I noticed that real Windows guests use TLB flush hypercall in a somewhat smarter way: When the flush needs to be performed on a subset of first 64 vCPUs or on all present vCPUs Windows avoids more expensive hypercalls which support sparse CPU sets and uses their 'cheap' counterparts. This means that HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED name is actually a misnomer: EX hypercalls (which support sparse CPU sets) are "available", not "recommended". This makes sense as they are actually harder to parse. Nothing stops us from being equally 'smart' in Linux too. Switch to doing cheaper hypercalls whenever possible. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Michael Kelley <mikelley@microsoft.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: Tianyu Lan <Tianyu.Lan@microsoft.com> Cc: devel@linuxdriverproject.org Cc: "H. Peter Anvin" <hpa@zytor.com> Link: https://lkml.kernel.org/r/20180621133238.30757-1-vkuznets@redhat.com
This commit is contained in:
parent
e0bc833d10
commit
0e4c88f376
@ -16,6 +16,8 @@
|
|||||||
/* Each gva in gva_list encodes up to 4096 pages to flush */
|
/* Each gva in gva_list encodes up to 4096 pages to flush */
|
||||||
#define HV_TLB_FLUSH_UNIT (4096 * PAGE_SIZE)
|
#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.
|
* Fills in gva_list starting from offset. Returns the number of items added.
|
||||||
@ -93,10 +95,24 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
|
|||||||
if (cpumask_equal(cpus, cpu_present_mask)) {
|
if (cpumask_equal(cpus, cpu_present_mask)) {
|
||||||
flush->flags |= HV_FLUSH_ALL_PROCESSORS;
|
flush->flags |= HV_FLUSH_ALL_PROCESSORS;
|
||||||
} else {
|
} 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) {
|
for_each_cpu(cpu, cpus) {
|
||||||
vcpu = hv_cpu_number_to_vp_number(cpu);
|
vcpu = hv_cpu_number_to_vp_number(cpu);
|
||||||
if (vcpu >= 64)
|
if (vcpu >= 64)
|
||||||
goto do_native;
|
goto do_ex_hypercall;
|
||||||
|
|
||||||
__set_bit(vcpu, (unsigned long *)
|
__set_bit(vcpu, (unsigned long *)
|
||||||
&flush->processor_mask);
|
&flush->processor_mask);
|
||||||
@ -123,7 +139,12 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
|
|||||||
status = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST,
|
status = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST,
|
||||||
gva_n, 0, flush, NULL);
|
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);
|
local_irq_restore(flags);
|
||||||
|
|
||||||
if (!(status & HV_HYPERCALL_RESULT_MASK))
|
if (!(status & HV_HYPERCALL_RESULT_MASK))
|
||||||
@ -132,35 +153,22 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
|
|||||||
native_flush_tlb_others(cpus, info);
|
native_flush_tlb_others(cpus, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
static u64 hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
||||||
const struct flush_tlb_info *info)
|
const struct flush_tlb_info *info)
|
||||||
{
|
{
|
||||||
int nr_bank = 0, max_gvas, gva_n;
|
int nr_bank = 0, max_gvas, gva_n;
|
||||||
struct hv_tlb_flush_ex **flush_pcpu;
|
struct hv_tlb_flush_ex **flush_pcpu;
|
||||||
struct hv_tlb_flush_ex *flush;
|
struct hv_tlb_flush_ex *flush;
|
||||||
u64 status = U64_MAX;
|
u64 status;
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
trace_hyperv_mmu_flush_tlb_others(cpus, info);
|
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED))
|
||||||
|
return U64_MAX;
|
||||||
if (!hv_hypercall_pg)
|
|
||||||
goto do_native;
|
|
||||||
|
|
||||||
if (cpumask_empty(cpus))
|
|
||||||
return;
|
|
||||||
|
|
||||||
local_irq_save(flags);
|
|
||||||
|
|
||||||
flush_pcpu = (struct hv_tlb_flush_ex **)
|
flush_pcpu = (struct hv_tlb_flush_ex **)
|
||||||
this_cpu_ptr(hyperv_pcpu_input_arg);
|
this_cpu_ptr(hyperv_pcpu_input_arg);
|
||||||
|
|
||||||
flush = *flush_pcpu;
|
flush = *flush_pcpu;
|
||||||
|
|
||||||
if (unlikely(!flush)) {
|
|
||||||
local_irq_restore(flags);
|
|
||||||
goto do_native;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info->mm) {
|
if (info->mm) {
|
||||||
/*
|
/*
|
||||||
* AddressSpace argument must match the CR3 with PCID bits
|
* AddressSpace argument must match the CR3 with PCID bits
|
||||||
@ -176,15 +184,8 @@ static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
|||||||
|
|
||||||
flush->hv_vp_set.valid_bank_mask = 0;
|
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;
|
||||||
flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
|
nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can flush not more than max_gvas with one hypercall. Flush the
|
* We can flush not more than max_gvas with one hypercall. Flush the
|
||||||
@ -213,12 +214,7 @@ static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
|
|||||||
gva_n, nr_bank, flush, NULL);
|
gva_n, nr_bank, flush, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
local_irq_restore(flags);
|
return status;
|
||||||
|
|
||||||
if (!(status & HV_HYPERCALL_RESULT_MASK))
|
|
||||||
return;
|
|
||||||
do_native:
|
|
||||||
native_flush_tlb_others(cpus, info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hyperv_setup_mmu_ops(void)
|
void hyperv_setup_mmu_ops(void)
|
||||||
@ -226,11 +222,6 @@ void hyperv_setup_mmu_ops(void)
|
|||||||
if (!(ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED))
|
if (!(ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) {
|
pr_info("Using hypercall for remote TLB flush\n");
|
||||||
pr_info("Using hypercall for remote TLB flush\n");
|
pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user