1 #define pr_fmt(fmt) "Hyper-V: " fmt
3 #include <linux/hyperv.h>
4 #include <linux/log2.h>
5 #include <linux/slab.h>
6 #include <linux/types.h>
8 #include <asm/fpu/api.h>
9 #include <asm/mshyperv.h>
11 #include <asm/tlbflush.h>
13 #define CREATE_TRACE_POINTS
14 #include <asm/trace/hyperv.h>
16 /* Each gva in gva_list encodes up to 4096 pages to flush */
17 #define HV_TLB_FLUSH_UNIT (4096 * PAGE_SIZE)
21 * Fills in gva_list starting from offset. Returns the number of items added.
23 static inline int fill_gva_list(u64 gva_list[], int offset,
24 unsigned long start, unsigned long end)
27 unsigned long cur = start, diff;
30 diff = end > cur ? end - cur : 0;
32 gva_list[gva_n] = cur & PAGE_MASK;
34 * Lower 12 bits encode the number of additional
35 * pages to flush (in addition to the 'cur' page).
37 if (diff >= HV_TLB_FLUSH_UNIT)
38 gva_list[gva_n] |= ~PAGE_MASK;
40 gva_list[gva_n] |= (diff - 1) >> PAGE_SHIFT;
42 cur += HV_TLB_FLUSH_UNIT;
47 return gva_n - offset;
50 static void hyperv_flush_tlb_others(const struct cpumask *cpus,
51 const struct flush_tlb_info *info)
53 int cpu, vcpu, gva_n, max_gvas;
54 struct hv_tlb_flush **flush_pcpu;
55 struct hv_tlb_flush *flush;
59 trace_hyperv_mmu_flush_tlb_others(cpus, info);
64 if (cpumask_empty(cpus))
67 local_irq_save(flags);
69 flush_pcpu = (struct hv_tlb_flush **)
70 this_cpu_ptr(hyperv_pcpu_input_arg);
74 if (unlikely(!flush)) {
75 local_irq_restore(flags);
81 * AddressSpace argument must match the CR3 with PCID bits
84 flush->address_space = virt_to_phys(info->mm->pgd);
85 flush->address_space &= CR3_ADDR_MASK;
88 flush->address_space = 0;
89 flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
92 flush->processor_mask = 0;
93 if (cpumask_equal(cpus, cpu_present_mask)) {
94 flush->flags |= HV_FLUSH_ALL_PROCESSORS;
96 for_each_cpu(cpu, cpus) {
97 vcpu = hv_cpu_number_to_vp_number(cpu);
101 __set_bit(vcpu, (unsigned long *)
102 &flush->processor_mask);
107 * We can flush not more than max_gvas with one hypercall. Flush the
108 * whole address space if we were asked to do more.
110 max_gvas = (PAGE_SIZE - sizeof(*flush)) / sizeof(flush->gva_list[0]);
112 if (info->end == TLB_FLUSH_ALL) {
113 flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
114 status = hv_do_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE,
116 } else if (info->end &&
117 ((info->end - info->start)/HV_TLB_FLUSH_UNIT) > max_gvas) {
118 status = hv_do_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE,
121 gva_n = fill_gva_list(flush->gva_list, 0,
122 info->start, info->end);
123 status = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST,
124 gva_n, 0, flush, NULL);
127 local_irq_restore(flags);
129 if (!(status & HV_HYPERCALL_RESULT_MASK))
132 native_flush_tlb_others(cpus, info);
135 static void hyperv_flush_tlb_others_ex(const struct cpumask *cpus,
136 const struct flush_tlb_info *info)
138 int nr_bank = 0, max_gvas, gva_n;
139 struct hv_tlb_flush_ex **flush_pcpu;
140 struct hv_tlb_flush_ex *flush;
141 u64 status = U64_MAX;
144 trace_hyperv_mmu_flush_tlb_others(cpus, info);
146 if (!hv_hypercall_pg)
149 if (cpumask_empty(cpus))
152 local_irq_save(flags);
154 flush_pcpu = (struct hv_tlb_flush_ex **)
155 this_cpu_ptr(hyperv_pcpu_input_arg);
159 if (unlikely(!flush)) {
160 local_irq_restore(flags);
166 * AddressSpace argument must match the CR3 with PCID bits
169 flush->address_space = virt_to_phys(info->mm->pgd);
170 flush->address_space &= CR3_ADDR_MASK;
173 flush->address_space = 0;
174 flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
177 flush->hv_vp_set.valid_bank_mask = 0;
179 if (!cpumask_equal(cpus, cpu_present_mask)) {
180 flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
181 nr_bank = cpumask_to_vpset(&(flush->hv_vp_set), cpus);
185 flush->hv_vp_set.format = HV_GENERIC_SET_ALL;
186 flush->flags |= HV_FLUSH_ALL_PROCESSORS;
190 * We can flush not more than max_gvas with one hypercall. Flush the
191 * whole address space if we were asked to do more.
194 (PAGE_SIZE - sizeof(*flush) - nr_bank *
195 sizeof(flush->hv_vp_set.bank_contents[0])) /
196 sizeof(flush->gva_list[0]);
198 if (info->end == TLB_FLUSH_ALL) {
199 flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
200 status = hv_do_rep_hypercall(
201 HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX,
202 0, nr_bank, flush, NULL);
203 } else if (info->end &&
204 ((info->end - info->start)/HV_TLB_FLUSH_UNIT) > max_gvas) {
205 status = hv_do_rep_hypercall(
206 HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX,
207 0, nr_bank, flush, NULL);
209 gva_n = fill_gva_list(flush->gva_list, nr_bank,
210 info->start, info->end);
211 status = hv_do_rep_hypercall(
212 HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX,
213 gva_n, nr_bank, flush, NULL);
216 local_irq_restore(flags);
218 if (!(status & HV_HYPERCALL_RESULT_MASK))
221 native_flush_tlb_others(cpus, info);
224 void hyperv_setup_mmu_ops(void)
226 if (!(ms_hyperv.hints & HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED))
229 if (!(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED)) {
230 pr_info("Using hypercall for remote TLB flush\n");
231 pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others;
233 pr_info("Using ext hypercall for remote TLB flush\n");
234 pv_mmu_ops.flush_tlb_others = hyperv_flush_tlb_others_ex;