|
Lines 23-28
Link Here
|
| 23 |
#include <linux/percpu.h> |
23 |
#include <linux/percpu.h> |
| 24 |
#include <linux/hardirq.h> |
24 |
#include <linux/hardirq.h> |
| 25 |
#include <linux/debugfs.h> |
25 |
#include <linux/debugfs.h> |
|
|
26 |
#include <linux/jump_label.h> |
| 27 |
#include <linux/printk.h> |
| 26 |
|
28 |
|
| 27 |
#include <asm/xen/hypercall.h> |
29 |
#include <asm/xen/hypercall.h> |
| 28 |
|
30 |
|
|
Lines 31-48
Link Here
|
| 31 |
|
33 |
|
| 32 |
#define MC_BATCH 32 |
34 |
#define MC_BATCH 32 |
| 33 |
|
35 |
|
| 34 |
#define MC_DEBUG 0 |
|
|
| 35 |
|
| 36 |
#define MC_ARGS (MC_BATCH * 16) |
36 |
#define MC_ARGS (MC_BATCH * 16) |
| 37 |
|
37 |
|
| 38 |
|
38 |
|
| 39 |
struct mc_buffer { |
39 |
struct mc_buffer { |
| 40 |
unsigned mcidx, argidx, cbidx; |
40 |
unsigned mcidx, argidx, cbidx; |
| 41 |
struct multicall_entry entries[MC_BATCH]; |
41 |
struct multicall_entry entries[MC_BATCH]; |
| 42 |
#if MC_DEBUG |
|
|
| 43 |
struct multicall_entry debug[MC_BATCH]; |
| 44 |
void *caller[MC_BATCH]; |
| 45 |
#endif |
| 46 |
unsigned char args[MC_ARGS]; |
42 |
unsigned char args[MC_ARGS]; |
| 47 |
struct callback { |
43 |
struct callback { |
| 48 |
void (*fn)(void *); |
44 |
void (*fn)(void *); |
|
Lines 50-62
struct mc_buffer {
Link Here
|
| 50 |
} callbacks[MC_BATCH]; |
46 |
} callbacks[MC_BATCH]; |
| 51 |
}; |
47 |
}; |
| 52 |
|
48 |
|
|
|
49 |
struct mc_debug_data { |
| 50 |
struct multicall_entry debug[MC_BATCH]; |
| 51 |
void *caller[MC_BATCH]; |
| 52 |
size_t argsz[MC_BATCH]; |
| 53 |
unsigned long *args[MC_BATCH]; |
| 54 |
}; |
| 55 |
|
| 53 |
static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); |
56 |
static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); |
|
|
57 |
static struct mc_debug_data __percpu *mc_debug_data; |
| 58 |
static struct mc_debug_data mc_debug_data_early __initdata; |
| 54 |
DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); |
59 |
DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); |
| 55 |
|
60 |
|
|
|
61 |
static struct static_key mc_debug __ro_after_init; |
| 62 |
static bool mc_debug_enabled __initdata; |
| 63 |
|
| 64 |
static int __init xen_parse_mc_debug(char *arg) |
| 65 |
{ |
| 66 |
mc_debug_enabled = true; |
| 67 |
static_key_slow_inc(&mc_debug); |
| 68 |
|
| 69 |
return 0; |
| 70 |
} |
| 71 |
early_param("xen_mc_debug", xen_parse_mc_debug); |
| 72 |
|
| 73 |
static int __init mc_debug_enable(void) |
| 74 |
{ |
| 75 |
struct mc_debug_data __percpu *mcdb; |
| 76 |
unsigned long flags; |
| 77 |
|
| 78 |
if (!mc_debug_enabled) |
| 79 |
return 0; |
| 80 |
|
| 81 |
mcdb = alloc_percpu(struct mc_debug_data); |
| 82 |
if (!mcdb) { |
| 83 |
pr_err("xen_mc_debug inactive\n"); |
| 84 |
static_key_slow_dec(&mc_debug); |
| 85 |
return -ENOMEM; |
| 86 |
} |
| 87 |
|
| 88 |
/* Be careful when switching to percpu debug data. */ |
| 89 |
local_irq_save(flags); |
| 90 |
xen_mc_flush(); |
| 91 |
mc_debug_data = mcdb; |
| 92 |
local_irq_restore(flags); |
| 93 |
|
| 94 |
pr_info("xen_mc_debug active\n"); |
| 95 |
|
| 96 |
return 0; |
| 97 |
} |
| 98 |
early_initcall(mc_debug_enable); |
| 99 |
|
| 100 |
/* Number of parameters of hypercalls used via multicalls. */ |
| 101 |
static const uint8_t hpcpars[] = { |
| 102 |
[__HYPERVISOR_mmu_update] = 4, |
| 103 |
[__HYPERVISOR_stack_switch] = 2, |
| 104 |
[__HYPERVISOR_fpu_taskswitch] = 1, |
| 105 |
[__HYPERVISOR_update_descriptor] = 2, |
| 106 |
[__HYPERVISOR_update_va_mapping] = 3, |
| 107 |
[__HYPERVISOR_mmuext_op] = 4, |
| 108 |
}; |
| 109 |
|
| 110 |
static void print_debug_data(struct mc_buffer *b, struct mc_debug_data *mcdb, |
| 111 |
int idx) |
| 112 |
{ |
| 113 |
unsigned int arg; |
| 114 |
unsigned int opidx = mcdb->debug[idx].op & 0xff; |
| 115 |
unsigned int pars = 0; |
| 116 |
|
| 117 |
pr_err(" call %2d: op=%lu result=%ld caller=%pS ", idx + 1, |
| 118 |
mcdb->debug[idx].op, b->entries[idx].result, mcdb->caller[idx]); |
| 119 |
if (opidx < ARRAY_SIZE(hpcpars)) |
| 120 |
pars = hpcpars[opidx]; |
| 121 |
if (pars) { |
| 122 |
pr_cont("pars="); |
| 123 |
for (arg = 0; arg < pars; arg++) |
| 124 |
pr_cont("%lx ", mcdb->debug[idx].args[arg]); |
| 125 |
} |
| 126 |
if (mcdb->argsz[idx]) { |
| 127 |
pr_cont("args="); |
| 128 |
for (arg = 0; arg < mcdb->argsz[idx] / 8; arg++) |
| 129 |
pr_cont("%lx ", mcdb->args[idx][arg]); |
| 130 |
} |
| 131 |
pr_cont("\n"); |
| 132 |
} |
| 133 |
|
| 134 |
static struct mc_debug_data * __ref get_mc_debug_ptr(void) |
| 135 |
{ |
| 136 |
if (likely(mc_debug_data)) |
| 137 |
return this_cpu_ptr(mc_debug_data); |
| 138 |
|
| 139 |
return &mc_debug_data_early; |
| 140 |
} |
| 141 |
|
| 56 |
void xen_mc_flush(void) |
142 |
void xen_mc_flush(void) |
| 57 |
{ |
143 |
{ |
| 58 |
struct mc_buffer *b = this_cpu_ptr(&mc_buffer); |
144 |
struct mc_buffer *b = this_cpu_ptr(&mc_buffer); |
| 59 |
struct multicall_entry *mc; |
145 |
struct multicall_entry *mc; |
|
|
146 |
struct mc_debug_data *mcdb = NULL; |
| 60 |
int ret = 0; |
147 |
int ret = 0; |
| 61 |
unsigned long flags; |
148 |
unsigned long flags; |
| 62 |
int i; |
149 |
int i; |
|
Lines 69-78
void xen_mc_flush(void)
Link Here
|
| 69 |
|
156 |
|
| 70 |
trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx); |
157 |
trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx); |
| 71 |
|
158 |
|
| 72 |
#if MC_DEBUG |
159 |
if (static_key_false(&mc_debug)) { |
| 73 |
memcpy(b->debug, b->entries, |
160 |
mcdb = get_mc_debug_ptr(); |
| 74 |
b->mcidx * sizeof(struct multicall_entry)); |
161 |
memcpy(mcdb->debug, b->entries, |
| 75 |
#endif |
162 |
b->mcidx * sizeof(struct multicall_entry)); |
|
|
163 |
} |
| 76 |
|
164 |
|
| 77 |
switch (b->mcidx) { |
165 |
switch (b->mcidx) { |
| 78 |
case 0: |
166 |
case 0: |
|
Lines 103-123
void xen_mc_flush(void)
Link Here
|
| 103 |
pr_err("%d of %d multicall(s) failed: cpu %d\n", |
191 |
pr_err("%d of %d multicall(s) failed: cpu %d\n", |
| 104 |
ret, b->mcidx, smp_processor_id()); |
192 |
ret, b->mcidx, smp_processor_id()); |
| 105 |
for (i = 0; i < b->mcidx; i++) { |
193 |
for (i = 0; i < b->mcidx; i++) { |
| 106 |
if (b->entries[i].result < 0) { |
194 |
if (static_key_false(&mc_debug)) { |
| 107 |
#if MC_DEBUG |
195 |
print_debug_data(b, mcdb, i); |
| 108 |
pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\t%pS\n", |
196 |
} else if (b->entries[i].result < 0) { |
| 109 |
i + 1, |
|
|
| 110 |
b->debug[i].op, |
| 111 |
b->debug[i].args[0], |
| 112 |
b->entries[i].result, |
| 113 |
b->caller[i]); |
| 114 |
#else |
| 115 |
pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\n", |
197 |
pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\n", |
| 116 |
i + 1, |
198 |
i + 1, |
| 117 |
b->entries[i].op, |
199 |
b->entries[i].op, |
| 118 |
b->entries[i].args[0], |
200 |
b->entries[i].args[0], |
| 119 |
b->entries[i].result); |
201 |
b->entries[i].result); |
| 120 |
#endif |
|
|
| 121 |
} |
202 |
} |
| 122 |
} |
203 |
} |
| 123 |
} |
204 |
} |
|
Lines 155-163
struct multicall_space __xen_mc_entry(size_t args)
Link Here
|
| 155 |
} |
236 |
} |
| 156 |
|
237 |
|
| 157 |
ret.mc = &b->entries[b->mcidx]; |
238 |
ret.mc = &b->entries[b->mcidx]; |
| 158 |
#if MC_DEBUG |
239 |
if (static_key_false(&mc_debug)) { |
| 159 |
b->caller[b->mcidx] = __builtin_return_address(0); |
240 |
struct mc_debug_data *mcdb = get_mc_debug_ptr(); |
| 160 |
#endif |
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 |
} |
| 161 |
b->mcidx++; |
246 |
b->mcidx++; |
| 162 |
ret.args = &b->args[argidx]; |
247 |
ret.args = &b->args[argidx]; |
| 163 |
b->argidx = argidx + args; |
248 |
b->argidx = argidx + args; |
| 164 |
- |
|
|