.. | .. |
---|
7 | 7 | * |
---|
8 | 8 | * Author : Lan Tianyu <Tianyu.Lan@microsoft.com> |
---|
9 | 9 | */ |
---|
| 10 | +#define pr_fmt(fmt) "Hyper-V: " fmt |
---|
10 | 11 | |
---|
11 | 12 | |
---|
12 | 13 | #include <linux/types.h> |
---|
.. | .. |
---|
54 | 55 | return ret; |
---|
55 | 56 | } |
---|
56 | 57 | EXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping); |
---|
| 58 | + |
---|
| 59 | +int hyperv_fill_flush_guest_mapping_list( |
---|
| 60 | + struct hv_guest_mapping_flush_list *flush, |
---|
| 61 | + u64 start_gfn, u64 pages) |
---|
| 62 | +{ |
---|
| 63 | + u64 cur = start_gfn; |
---|
| 64 | + u64 additional_pages; |
---|
| 65 | + int gpa_n = 0; |
---|
| 66 | + |
---|
| 67 | + do { |
---|
| 68 | + /* |
---|
| 69 | + * If flush requests exceed max flush count, go back to |
---|
| 70 | + * flush tlbs without range. |
---|
| 71 | + */ |
---|
| 72 | + if (gpa_n >= HV_MAX_FLUSH_REP_COUNT) |
---|
| 73 | + return -ENOSPC; |
---|
| 74 | + |
---|
| 75 | + additional_pages = min_t(u64, pages, HV_MAX_FLUSH_PAGES) - 1; |
---|
| 76 | + |
---|
| 77 | + flush->gpa_list[gpa_n].page.additional_pages = additional_pages; |
---|
| 78 | + flush->gpa_list[gpa_n].page.largepage = false; |
---|
| 79 | + flush->gpa_list[gpa_n].page.basepfn = cur; |
---|
| 80 | + |
---|
| 81 | + pages -= additional_pages + 1; |
---|
| 82 | + cur += additional_pages + 1; |
---|
| 83 | + gpa_n++; |
---|
| 84 | + } while (pages > 0); |
---|
| 85 | + |
---|
| 86 | + return gpa_n; |
---|
| 87 | +} |
---|
| 88 | +EXPORT_SYMBOL_GPL(hyperv_fill_flush_guest_mapping_list); |
---|
| 89 | + |
---|
| 90 | +int hyperv_flush_guest_mapping_range(u64 as, |
---|
| 91 | + hyperv_fill_flush_list_func fill_flush_list_func, void *data) |
---|
| 92 | +{ |
---|
| 93 | + struct hv_guest_mapping_flush_list **flush_pcpu; |
---|
| 94 | + struct hv_guest_mapping_flush_list *flush; |
---|
| 95 | + u64 status = 0; |
---|
| 96 | + unsigned long flags; |
---|
| 97 | + int ret = -ENOTSUPP; |
---|
| 98 | + int gpa_n = 0; |
---|
| 99 | + |
---|
| 100 | + if (!hv_hypercall_pg || !fill_flush_list_func) |
---|
| 101 | + goto fault; |
---|
| 102 | + |
---|
| 103 | + local_irq_save(flags); |
---|
| 104 | + |
---|
| 105 | + flush_pcpu = (struct hv_guest_mapping_flush_list **) |
---|
| 106 | + this_cpu_ptr(hyperv_pcpu_input_arg); |
---|
| 107 | + |
---|
| 108 | + flush = *flush_pcpu; |
---|
| 109 | + if (unlikely(!flush)) { |
---|
| 110 | + local_irq_restore(flags); |
---|
| 111 | + goto fault; |
---|
| 112 | + } |
---|
| 113 | + |
---|
| 114 | + flush->address_space = as; |
---|
| 115 | + flush->flags = 0; |
---|
| 116 | + |
---|
| 117 | + gpa_n = fill_flush_list_func(flush, data); |
---|
| 118 | + if (gpa_n < 0) { |
---|
| 119 | + local_irq_restore(flags); |
---|
| 120 | + goto fault; |
---|
| 121 | + } |
---|
| 122 | + |
---|
| 123 | + status = hv_do_rep_hypercall(HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST, |
---|
| 124 | + gpa_n, 0, flush, NULL); |
---|
| 125 | + |
---|
| 126 | + local_irq_restore(flags); |
---|
| 127 | + |
---|
| 128 | + if (!(status & HV_HYPERCALL_RESULT_MASK)) |
---|
| 129 | + ret = 0; |
---|
| 130 | + else |
---|
| 131 | + ret = status; |
---|
| 132 | +fault: |
---|
| 133 | + trace_hyperv_nested_flush_guest_mapping_range(as, ret); |
---|
| 134 | + return ret; |
---|
| 135 | +} |
---|
| 136 | +EXPORT_SYMBOL_GPL(hyperv_flush_guest_mapping_range); |
---|