1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/types.h> |
3 | #include <linux/vmalloc.h> |
4 | #include <linux/mm.h> |
5 | #include <linux/clockchips.h> |
6 | #include <linux/acpi.h> |
7 | #include <linux/hyperv.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/cpuhotplug.h> |
10 | #include <linux/minmax.h> |
11 | #include <asm/hypervisor.h> |
12 | #include <asm/mshyperv.h> |
13 | #include <asm/apic.h> |
14 | |
15 | #include <asm/trace/hyperv.h> |
16 | |
17 | /* |
18 | * See struct hv_deposit_memory. The first u64 is partition ID, the rest |
19 | * are GPAs. |
20 | */ |
21 | #define HV_DEPOSIT_MAX (HV_HYP_PAGE_SIZE / sizeof(u64) - 1) |
22 | |
23 | /* Deposits exact number of pages. Must be called with interrupts enabled. */ |
24 | int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages) |
25 | { |
26 | struct page **pages, *page; |
27 | int *counts; |
28 | int num_allocations; |
29 | int i, j, page_count; |
30 | int order; |
31 | u64 status; |
32 | int ret; |
33 | u64 base_pfn; |
34 | struct hv_deposit_memory *input_page; |
35 | unsigned long flags; |
36 | |
37 | if (num_pages > HV_DEPOSIT_MAX) |
38 | return -E2BIG; |
39 | if (!num_pages) |
40 | return 0; |
41 | |
42 | /* One buffer for page pointers and counts */ |
43 | page = alloc_page(GFP_KERNEL); |
44 | if (!page) |
45 | return -ENOMEM; |
46 | pages = page_address(page); |
47 | |
48 | counts = kcalloc(HV_DEPOSIT_MAX, size: sizeof(int), GFP_KERNEL); |
49 | if (!counts) { |
50 | free_page((unsigned long)pages); |
51 | return -ENOMEM; |
52 | } |
53 | |
54 | /* Allocate all the pages before disabling interrupts */ |
55 | i = 0; |
56 | |
57 | while (num_pages) { |
58 | /* Find highest order we can actually allocate */ |
59 | order = 31 - __builtin_clz(num_pages); |
60 | |
61 | while (1) { |
62 | pages[i] = alloc_pages_node(nid: node, GFP_KERNEL, order); |
63 | if (pages[i]) |
64 | break; |
65 | if (!order) { |
66 | ret = -ENOMEM; |
67 | num_allocations = i; |
68 | goto err_free_allocations; |
69 | } |
70 | --order; |
71 | } |
72 | |
73 | split_page(page: pages[i], order); |
74 | counts[i] = 1 << order; |
75 | num_pages -= counts[i]; |
76 | i++; |
77 | } |
78 | num_allocations = i; |
79 | |
80 | local_irq_save(flags); |
81 | |
82 | input_page = *this_cpu_ptr(hyperv_pcpu_input_arg); |
83 | |
84 | input_page->partition_id = partition_id; |
85 | |
86 | /* Populate gpa_page_list - these will fit on the input page */ |
87 | for (i = 0, page_count = 0; i < num_allocations; ++i) { |
88 | base_pfn = page_to_pfn(pages[i]); |
89 | for (j = 0; j < counts[i]; ++j, ++page_count) |
90 | input_page->gpa_page_list[page_count] = base_pfn + j; |
91 | } |
92 | status = hv_do_rep_hypercall(HVCALL_DEPOSIT_MEMORY, |
93 | rep_count: page_count, varhead_size: 0, input: input_page, NULL); |
94 | local_irq_restore(flags); |
95 | if (!hv_result_success(status)) { |
96 | pr_err("Failed to deposit pages: %lld\n" , status); |
97 | ret = hv_result(status); |
98 | goto err_free_allocations; |
99 | } |
100 | |
101 | ret = 0; |
102 | goto free_buf; |
103 | |
104 | err_free_allocations: |
105 | for (i = 0; i < num_allocations; ++i) { |
106 | base_pfn = page_to_pfn(pages[i]); |
107 | for (j = 0; j < counts[i]; ++j) |
108 | __free_page(pfn_to_page(base_pfn + j)); |
109 | } |
110 | |
111 | free_buf: |
112 | free_page((unsigned long)pages); |
113 | kfree(objp: counts); |
114 | return ret; |
115 | } |
116 | |
117 | int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) |
118 | { |
119 | struct hv_add_logical_processor_in *input; |
120 | struct hv_add_logical_processor_out *output; |
121 | u64 status; |
122 | unsigned long flags; |
123 | int ret = HV_STATUS_SUCCESS; |
124 | int pxm = node_to_pxm(node); |
125 | |
126 | /* |
127 | * When adding a logical processor, the hypervisor may return |
128 | * HV_STATUS_INSUFFICIENT_MEMORY. When that happens, we deposit more |
129 | * pages and retry. |
130 | */ |
131 | do { |
132 | local_irq_save(flags); |
133 | |
134 | input = *this_cpu_ptr(hyperv_pcpu_input_arg); |
135 | /* We don't do anything with the output right now */ |
136 | output = *this_cpu_ptr(hyperv_pcpu_output_arg); |
137 | |
138 | input->lp_index = lp_index; |
139 | input->apic_id = apic_id; |
140 | input->flags = 0; |
141 | input->proximity_domain_info.domain_id = pxm; |
142 | input->proximity_domain_info.flags.reserved = 0; |
143 | input->proximity_domain_info.flags.proximity_info_valid = 1; |
144 | input->proximity_domain_info.flags.proximity_preferred = 1; |
145 | status = hv_do_hypercall(HVCALL_ADD_LOGICAL_PROCESSOR, |
146 | inputaddr: input, outputaddr: output); |
147 | local_irq_restore(flags); |
148 | |
149 | if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { |
150 | if (!hv_result_success(status)) { |
151 | pr_err("%s: cpu %u apic ID %u, %lld\n" , __func__, |
152 | lp_index, apic_id, status); |
153 | ret = hv_result(status); |
154 | } |
155 | break; |
156 | } |
157 | ret = hv_call_deposit_pages(node, partition_id: hv_current_partition_id, num_pages: 1); |
158 | } while (!ret); |
159 | |
160 | return ret; |
161 | } |
162 | |
163 | int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) |
164 | { |
165 | struct hv_create_vp *input; |
166 | u64 status; |
167 | unsigned long irq_flags; |
168 | int ret = HV_STATUS_SUCCESS; |
169 | int pxm = node_to_pxm(node); |
170 | |
171 | /* Root VPs don't seem to need pages deposited */ |
172 | if (partition_id != hv_current_partition_id) { |
173 | /* The value 90 is empirically determined. It may change. */ |
174 | ret = hv_call_deposit_pages(node, partition_id, num_pages: 90); |
175 | if (ret) |
176 | return ret; |
177 | } |
178 | |
179 | do { |
180 | local_irq_save(irq_flags); |
181 | |
182 | input = *this_cpu_ptr(hyperv_pcpu_input_arg); |
183 | |
184 | input->partition_id = partition_id; |
185 | input->vp_index = vp_index; |
186 | input->flags = flags; |
187 | input->subnode_type = HvSubnodeAny; |
188 | if (node != NUMA_NO_NODE) { |
189 | input->proximity_domain_info.domain_id = pxm; |
190 | input->proximity_domain_info.flags.reserved = 0; |
191 | input->proximity_domain_info.flags.proximity_info_valid = 1; |
192 | input->proximity_domain_info.flags.proximity_preferred = 1; |
193 | } else { |
194 | input->proximity_domain_info.as_uint64 = 0; |
195 | } |
196 | status = hv_do_hypercall(HVCALL_CREATE_VP, inputaddr: input, NULL); |
197 | local_irq_restore(irq_flags); |
198 | |
199 | if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) { |
200 | if (!hv_result_success(status)) { |
201 | pr_err("%s: vcpu %u, lp %u, %lld\n" , __func__, |
202 | vp_index, flags, status); |
203 | ret = hv_result(status); |
204 | } |
205 | break; |
206 | } |
207 | ret = hv_call_deposit_pages(node, partition_id, num_pages: 1); |
208 | |
209 | } while (!ret); |
210 | |
211 | return ret; |
212 | } |
213 | |
214 | |