1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Xen hypercall batching.
4 *
5 * Xen allows multiple hypercalls to be issued at once, using the
6 * multicall interface. This allows the cost of trapping into the
7 * hypervisor to be amortized over several calls.
8 *
9 * This file implements a simple interface for multicalls. There's a
10 * per-cpu buffer of outstanding multicalls. When you want to queue a
11 * multicall for issuing, you can allocate a multicall slot for the
12 * call and its arguments, along with storage for space which is
13 * pointed to by the arguments (for passing pointers to structures,
14 * etc). When the multicall is actually issued, all the space for the
15 * commands and allocated memory is freed for reuse.
16 *
17 * Multicalls are flushed whenever any of the buffers get full, or
18 * when explicitly requested. There's no way to get per-multicall
19 * return results back. It will BUG if any of the multicalls fail.
20 *
21 * Jeremy Fitzhardinge <[email protected]>, XenSource Inc, 2007
22 */
23 #include <linux/percpu.h>
24 #include <linux/hardirq.h>
25 #include <linux/debugfs.h>
26 #include <linux/jump_label.h>
27 #include <linux/printk.h>
28
29 #include <asm/xen/hypercall.h>
30
31 #include "xen-ops.h"
32
33 #define MC_BATCH 32
34
35 #define MC_ARGS (MC_BATCH * 16)
36
37
38 struct mc_buffer {
39 unsigned mcidx, argidx, cbidx;
40 struct multicall_entry entries[MC_BATCH];
41 unsigned char args[MC_ARGS];
42 struct callback {
43 void (*fn)(void *);
44 void *data;
45 } callbacks[MC_BATCH];
46 };
47
48 struct mc_debug_data {
49 struct multicall_entry entries[MC_BATCH];
50 void *caller[MC_BATCH];
51 size_t argsz[MC_BATCH];
52 unsigned long *args[MC_BATCH];
53 };
54
55 static DEFINE_PER_CPU(struct mc_buffer, mc_buffer);
56 static struct mc_debug_data mc_debug_data_early __initdata;
57 static struct mc_debug_data __percpu *mc_debug_data_ptr;
58 DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags);
59
60 static struct static_key mc_debug __ro_after_init;
61 static bool mc_debug_enabled __initdata;
62
get_mc_debug(void)63 static struct mc_debug_data * __ref get_mc_debug(void)
64 {
65 if (!mc_debug_data_ptr)
66 return &mc_debug_data_early;
67
68 return this_cpu_ptr(mc_debug_data_ptr);
69 }
70
xen_parse_mc_debug(char * arg)71 static int __init xen_parse_mc_debug(char *arg)
72 {
73 mc_debug_enabled = true;
74 static_key_slow_inc(&mc_debug);
75
76 return 0;
77 }
78 early_param("xen_mc_debug", xen_parse_mc_debug);
79
mc_debug_enable(void)80 static int __init mc_debug_enable(void)
81 {
82 unsigned long flags;
83 struct mc_debug_data __percpu *mcdb;
84
85 if (!mc_debug_enabled)
86 return 0;
87
88 mcdb = alloc_percpu(struct mc_debug_data);
89 if (!mcdb) {
90 pr_err("xen_mc_debug inactive\n");
91 static_key_slow_dec(&mc_debug);
92 return -ENOMEM;
93 }
94
95 /* Be careful when switching to percpu debug data. */
96 local_irq_save(flags);
97 xen_mc_flush();
98 mc_debug_data_ptr = mcdb;
99 local_irq_restore(flags);
100
101 pr_info("xen_mc_debug active\n");
102
103 return 0;
104 }
105 early_initcall(mc_debug_enable);
106
107 /* Number of parameters of hypercalls used via multicalls. */
108 static const uint8_t hpcpars[] = {
109 [__HYPERVISOR_mmu_update] = 4,
110 [__HYPERVISOR_stack_switch] = 2,
111 [__HYPERVISOR_fpu_taskswitch] = 1,
112 [__HYPERVISOR_update_descriptor] = 2,
113 [__HYPERVISOR_update_va_mapping] = 3,
114 [__HYPERVISOR_mmuext_op] = 4,
115 };
116
print_debug_data(struct mc_buffer * b,struct mc_debug_data * mcdb,int idx)117 static void print_debug_data(struct mc_buffer *b, struct mc_debug_data *mcdb,
118 int idx)
119 {
120 unsigned int arg;
121 unsigned int opidx = mcdb->entries[idx].op & 0xff;
122 unsigned int pars = 0;
123
124 pr_err(" call %2d: op=%lu result=%ld caller=%pS ", idx + 1,
125 mcdb->entries[idx].op, b->entries[idx].result,
126 mcdb->caller[idx]);
127 if (opidx < ARRAY_SIZE(hpcpars))
128 pars = hpcpars[opidx];
129 if (pars) {
130 pr_cont("pars=");
131 for (arg = 0; arg < pars; arg++)
132 pr_cont("%lx ", mcdb->entries[idx].args[arg]);
133 }
134 if (mcdb->argsz[idx]) {
135 pr_cont("args=");
136 for (arg = 0; arg < mcdb->argsz[idx] / 8; arg++)
137 pr_cont("%lx ", mcdb->args[idx][arg]);
138 }
139 pr_cont("\n");
140 }
141
xen_mc_flush(void)142 void xen_mc_flush(void)
143 {
144 struct mc_buffer *b = this_cpu_ptr(&mc_buffer);
145 struct multicall_entry *mc;
146 struct mc_debug_data *mcdb = NULL;
147 int ret = 0;
148 unsigned long flags;
149 int i;
150
151 BUG_ON(preemptible());
152
153 /* Disable interrupts in case someone comes in and queues
154 something in the middle */
155 local_irq_save(flags);
156
157 trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx);
158
159 if (static_key_false(&mc_debug)) {
160 mcdb = get_mc_debug();
161 memcpy(mcdb->entries, b->entries,
162 b->mcidx * sizeof(struct multicall_entry));
163 }
164
165 switch (b->mcidx) {
166 case 0:
167 /* no-op */
168 BUG_ON(b->argidx != 0);
169 break;
170
171 case 1:
172 /* Singleton multicall - bypass multicall machinery
173 and just do the call directly. */
174 mc = &b->entries[0];
175
176 mc->result = xen_single_call(mc->op, mc->args[0], mc->args[1],
177 mc->args[2], mc->args[3],
178 mc->args[4]);
179 ret = mc->result < 0;
180 break;
181
182 default:
183 if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0)
184 BUG();
185 for (i = 0; i < b->mcidx; i++)
186 if (b->entries[i].result < 0)
187 ret++;
188 }
189
190 if (WARN_ON(ret)) {
191 pr_err("%d of %d multicall(s) failed: cpu %d\n",
192 ret, b->mcidx, smp_processor_id());
193 for (i = 0; i < b->mcidx; i++) {
194 if (static_key_false(&mc_debug)) {
195 print_debug_data(b, mcdb, i);
196 } else if (b->entries[i].result < 0) {
197 pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\n",
198 i + 1,
199 b->entries[i].op,
200 b->entries[i].args[0],
201 b->entries[i].result);
202 }
203 }
204 }
205
206 b->mcidx = 0;
207 b->argidx = 0;
208
209 for (i = 0; i < b->cbidx; i++) {
210 struct callback *cb = &b->callbacks[i];
211
212 (*cb->fn)(cb->data);
213 }
214 b->cbidx = 0;
215
216 local_irq_restore(flags);
217 }
218
__xen_mc_entry(size_t args)219 struct multicall_space __xen_mc_entry(size_t args)
220 {
221 struct mc_buffer *b = this_cpu_ptr(&mc_buffer);
222 struct multicall_space ret;
223 unsigned argidx = roundup(b->argidx, sizeof(u64));
224
225 trace_xen_mc_entry_alloc(args);
226
227 BUG_ON(preemptible());
228 BUG_ON(b->argidx >= MC_ARGS);
229
230 if (unlikely(b->mcidx == MC_BATCH ||
231 (argidx + args) >= MC_ARGS)) {
232 trace_xen_mc_flush_reason((b->mcidx == MC_BATCH) ?
233 XEN_MC_FL_BATCH : XEN_MC_FL_ARGS);
234 xen_mc_flush();
235 argidx = roundup(b->argidx, sizeof(u64));
236 }
237
238 ret.mc = &b->entries[b->mcidx];
239 if (static_key_false(&mc_debug)) {
240 struct mc_debug_data *mcdb = get_mc_debug();
241
242 mcdb->caller[b->mcidx] = __builtin_return_address(0);
243 mcdb->argsz[b->mcidx] = args;
244 mcdb->args[b->mcidx] = (unsigned long *)(&b->args[argidx]);
245 }
246 b->mcidx++;
247 ret.args = &b->args[argidx];
248 b->argidx = argidx + args;
249
250 BUG_ON(b->argidx >= MC_ARGS);
251 return ret;
252 }
253
xen_mc_extend_args(unsigned long op,size_t size)254 struct multicall_space xen_mc_extend_args(unsigned long op, size_t size)
255 {
256 struct mc_buffer *b = this_cpu_ptr(&mc_buffer);
257 struct multicall_space ret = { NULL, NULL };
258
259 BUG_ON(preemptible());
260 BUG_ON(b->argidx >= MC_ARGS);
261
262 if (unlikely(b->mcidx == 0 ||
263 b->entries[b->mcidx - 1].op != op)) {
264 trace_xen_mc_extend_args(op, size, XEN_MC_XE_BAD_OP);
265 goto out;
266 }
267
268 if (unlikely((b->argidx + size) >= MC_ARGS)) {
269 trace_xen_mc_extend_args(op, size, XEN_MC_XE_NO_SPACE);
270 goto out;
271 }
272
273 ret.mc = &b->entries[b->mcidx - 1];
274 ret.args = &b->args[b->argidx];
275 b->argidx += size;
276
277 BUG_ON(b->argidx >= MC_ARGS);
278
279 trace_xen_mc_extend_args(op, size, XEN_MC_XE_OK);
280 out:
281 return ret;
282 }
283
xen_mc_callback(void (* fn)(void *),void * data)284 void xen_mc_callback(void (*fn)(void *), void *data)
285 {
286 struct mc_buffer *b = this_cpu_ptr(&mc_buffer);
287 struct callback *cb;
288
289 if (b->cbidx == MC_BATCH) {
290 trace_xen_mc_flush_reason(XEN_MC_FL_CALLBACK);
291 xen_mc_flush();
292 }
293
294 trace_xen_mc_callback(fn, data);
295
296 cb = &b->callbacks[b->cbidx++];
297 cb->fn = fn;
298 cb->data = data;
299 }
300