-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86/hyper-v: Use hypercall for remote TLB flush
Hyper-V host can suggest us to use hypercall for doing remote TLB flush, this is supposed to work faster than IPIs. Implementation details: to do HvFlushVirtualAddress{Space,List} hypercalls we need to put the input somewhere in memory and we don't really want to have memory allocation on each call so we pre-allocate per cpu memory areas on boot. pv_ops patching is happening very early so we need to separate hyperv_setup_mmu_ops() and hyper_alloc_mmu(). It is possible and easy to implement local TLB flushing too and there is even a hint for that. However, I don't see a room for optimization on the host side as both hypercall and native tlb flush will result in vmexit. The hint is also not set on modern Hyper-V versions. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Stephen Hemminger <sthemmin@microsoft.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Haiyang Zhang <haiyangz@microsoft.com> Cc: Jork Loeser <Jork.Loeser@microsoft.com> Cc: K. Y. Srinivasan <kys@microsoft.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Simon Xiao <sixiao@microsoft.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: devel@linuxdriverproject.org Link: http://lkml.kernel.org/r/20170802160921.21791-8-vkuznets@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
- Loading branch information
Vitaly Kuznetsov
authored and
Ingo Molnar
committed
Aug 10, 2017
1 parent
7415aea
commit 2ffd9e3
Showing
7 changed files
with
153 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
obj-y := hv_init.o | ||
obj-y := hv_init.o mmu.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#define pr_fmt(fmt) "Hyper-V: " fmt | ||
|
||
#include <linux/hyperv.h> | ||
#include <linux/log2.h> | ||
#include <linux/slab.h> | ||
#include <linux/types.h> | ||
|
||
#include <asm/fpu/api.h> | ||
#include <asm/mshyperv.h> | ||
#include <asm/msr.h> | ||
#include <asm/tlbflush.h> | ||
|
||
/* HvFlushVirtualAddressSpace, HvFlushVirtualAddressList hypercalls */ | ||
struct hv_flush_pcpu { | ||
u64 address_space; | ||
u64 flags; | ||
u64 processor_mask; | ||
u64 gva_list[]; | ||
}; | ||
|
||
/* Each gva in gva_list encodes up to 4096 pages to flush */ | ||
#define HV_TLB_FLUSH_UNIT (4096 * PAGE_SIZE) | ||
|
||
static struct hv_flush_pcpu __percpu *pcpu_flush; | ||
|
||
/* | ||
* Fills in gva_list starting from offset. Returns the number of items added. | ||
*/ | ||
static inline int fill_gva_list(u64 gva_list[], int offset, | ||
unsigned long start, unsigned long end) | ||
{ | ||
int gva_n = offset; | ||
unsigned long cur = start, diff; | ||
|
||
do { | ||
diff = end > cur ? end - cur : 0; | ||
|
||
gva_list[gva_n] = cur & PAGE_MASK; | ||
/* | ||
* Lower 12 bits encode the number of additional | ||
* pages to flush (in addition to the 'cur' page). | ||
*/ | ||
if (diff >= HV_TLB_FLUSH_UNIT) | ||
gva_list[gva_n] |= ~PAGE_MASK; | ||
else if (diff) | ||
gva_list[gva_n] |= (diff - 1) >> PAGE_SHIFT; | ||
|
||
cur += HV_TLB_FLUSH_UNIT; | ||
gva_n++; | ||
|
||
} while (cur < end); | ||
|
||
return gva_n - offset; | ||
} | ||
|
||
static void hyperv_flush_tlb_others(const struct cpumask *cpus, | ||
const struct flush_tlb_info *info) | ||
{ | ||
int cpu, vcpu, gva_n, max_gvas; | ||
struct hv_flush_pcpu *flush; | ||
u64 status = U64_MAX; | ||
unsigned long flags; | ||
|
||
if (!pcpu_flush || !hv_hypercall_pg) | ||
goto do_native; | ||
|
||
if (cpumask_empty(cpus)) | ||
return; | ||
|
||
local_irq_save(flags); | ||
|
||
flush = this_cpu_ptr(pcpu_flush); | ||
|
||
if (info->mm) { | ||
flush->address_space = virt_to_phys(info->mm->pgd); | ||
flush->flags = 0; | ||
} else { | ||
flush->address_space = 0; | ||
flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES; | ||
} | ||
|
||
flush->processor_mask = 0; | ||
if (cpumask_equal(cpus, cpu_present_mask)) { | ||
flush->flags |= HV_FLUSH_ALL_PROCESSORS; | ||
} else { | ||
for_each_cpu(cpu, cpus) { | ||
vcpu = hv_cpu_number_to_vp_number(cpu); | ||
if (vcpu >= 64) | ||
goto do_native; | ||
|
||
__set_bit(vcpu, (unsigned long *) | ||
&flush->processor_mask); | ||
} | ||
} | ||
|
||
/* | ||
* We can flush not more than max_gvas with one hypercall. Flush the | ||
* whole address space if we were asked to do more. | ||
*/ | ||
max_gvas = (PAGE_SIZE - sizeof(*flush)) / sizeof(flush->gva_list[0]); | ||
|
||
if (info->end == TLB_FLUSH_ALL) { | ||
flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY; | ||
status = hv_do_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE, | ||
flush, NULL); | ||
} else if (info->end && | ||
((info->end - info->start)/HV_TLB_FLUSH_UNIT) > max_gvas) { | ||
status = hv_do_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE, | ||
flush, NULL); | ||
} else { | ||
gva_n = fill_gva_list(flush->gva_list, 0, | ||
info->start, info->end); | ||
status = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST, | ||
gva_n, 0, flush, NULL); | ||
} | ||
|
||
local_irq_restore(flags); | ||
|
||
if (!(status & HV_HYPERCALL_RESULT_MASK)) | ||
return; | ||
do_native: | ||
native_flush_tlb_others(cpus, info); | ||
} | ||
|
||
void hyperv_setup_mmu_ops(void) | ||
{ | ||
if (ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED) { | ||
pr_info("Using hypercall for remote TLB flush\n"); | ||
pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others; | ||
setup_clear_cpu_cap(X86_FEATURE_PCID); | ||
} | ||
} | ||
|
||
void hyper_alloc_mmu(void) | ||
{ | ||
if (ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED) | ||
pcpu_flush = __alloc_percpu(PAGE_SIZE, PAGE_SIZE); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters