19702785aSThomas Gleixner /* 29702785aSThomas Gleixner * Xen hypercall batching. 39702785aSThomas Gleixner * 49702785aSThomas Gleixner * Xen allows multiple hypercalls to be issued at once, using the 59702785aSThomas Gleixner * multicall interface. This allows the cost of trapping into the 69702785aSThomas Gleixner * hypervisor to be amortized over several calls. 79702785aSThomas Gleixner * 89702785aSThomas Gleixner * This file implements a simple interface for multicalls. There's a 99702785aSThomas Gleixner * per-cpu buffer of outstanding multicalls. When you want to queue a 109702785aSThomas Gleixner * multicall for issuing, you can allocate a multicall slot for the 119702785aSThomas Gleixner * call and its arguments, along with storage for space which is 129702785aSThomas Gleixner * pointed to by the arguments (for passing pointers to structures, 139702785aSThomas Gleixner * etc). When the multicall is actually issued, all the space for the 149702785aSThomas Gleixner * commands and allocated memory is freed for reuse. 159702785aSThomas Gleixner * 169702785aSThomas Gleixner * Multicalls are flushed whenever any of the buffers get full, or 179702785aSThomas Gleixner * when explicitly requested. There's no way to get per-multicall 189702785aSThomas Gleixner * return results back. It will BUG if any of the multicalls fail. 199702785aSThomas Gleixner * 209702785aSThomas Gleixner * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 219702785aSThomas Gleixner */ 229702785aSThomas Gleixner #include <linux/percpu.h> 239702785aSThomas Gleixner #include <linux/hardirq.h> 24994025caSJeremy Fitzhardinge #include <linux/debugfs.h> 259702785aSThomas Gleixner 269702785aSThomas Gleixner #include <asm/xen/hypercall.h> 279702785aSThomas Gleixner 289702785aSThomas Gleixner #include "multicalls.h" 29994025caSJeremy Fitzhardinge #include "debugfs.h" 30994025caSJeremy Fitzhardinge 31994025caSJeremy Fitzhardinge #define MC_BATCH 32 329702785aSThomas Gleixner 33a122d623SJeremy Fitzhardinge #define MC_DEBUG 1 34a122d623SJeremy Fitzhardinge 35400d3494SJeremy Fitzhardinge #define MC_ARGS (MC_BATCH * 16) 369702785aSThomas Gleixner 37994025caSJeremy Fitzhardinge 389702785aSThomas Gleixner struct mc_buffer { 399702785aSThomas Gleixner struct multicall_entry entries[MC_BATCH]; 40a122d623SJeremy Fitzhardinge #if MC_DEBUG 41a122d623SJeremy Fitzhardinge struct multicall_entry debug[MC_BATCH]; 42b93d51dcSIan Campbell void *caller[MC_BATCH]; 43a122d623SJeremy Fitzhardinge #endif 44400d3494SJeremy Fitzhardinge unsigned char args[MC_ARGS]; 4591e0c5f3SJeremy Fitzhardinge struct callback { 4691e0c5f3SJeremy Fitzhardinge void (*fn)(void *); 4791e0c5f3SJeremy Fitzhardinge void *data; 4891e0c5f3SJeremy Fitzhardinge } callbacks[MC_BATCH]; 4991e0c5f3SJeremy Fitzhardinge unsigned mcidx, argidx, cbidx; 509702785aSThomas Gleixner }; 519702785aSThomas Gleixner 529702785aSThomas Gleixner static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); 539702785aSThomas Gleixner DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); 549702785aSThomas Gleixner 559702785aSThomas Gleixner void xen_mc_flush(void) 569702785aSThomas Gleixner { 579702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 589702785aSThomas Gleixner int ret = 0; 599702785aSThomas Gleixner unsigned long flags; 6091e0c5f3SJeremy Fitzhardinge int i; 619702785aSThomas Gleixner 629702785aSThomas Gleixner BUG_ON(preemptible()); 639702785aSThomas Gleixner 649702785aSThomas Gleixner /* Disable interrupts in case someone comes in and queues 659702785aSThomas Gleixner something in the middle */ 669702785aSThomas Gleixner local_irq_save(flags); 679702785aSThomas Gleixner 68*c796f213SJeremy Fitzhardinge trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx); 69*c796f213SJeremy Fitzhardinge 709702785aSThomas Gleixner if (b->mcidx) { 71a122d623SJeremy Fitzhardinge #if MC_DEBUG 72a122d623SJeremy Fitzhardinge memcpy(b->debug, b->entries, 73a122d623SJeremy Fitzhardinge b->mcidx * sizeof(struct multicall_entry)); 74a122d623SJeremy Fitzhardinge #endif 75a122d623SJeremy Fitzhardinge 769702785aSThomas Gleixner if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) 779702785aSThomas Gleixner BUG(); 789702785aSThomas Gleixner for (i = 0; i < b->mcidx; i++) 799702785aSThomas Gleixner if (b->entries[i].result < 0) 809702785aSThomas Gleixner ret++; 81a122d623SJeremy Fitzhardinge 82a122d623SJeremy Fitzhardinge #if MC_DEBUG 83a122d623SJeremy Fitzhardinge if (ret) { 84a122d623SJeremy Fitzhardinge printk(KERN_ERR "%d multicall(s) failed: cpu %d\n", 85a122d623SJeremy Fitzhardinge ret, smp_processor_id()); 868ba6c2b0SJeremy Fitzhardinge dump_stack(); 87a122d623SJeremy Fitzhardinge for (i = 0; i < b->mcidx; i++) { 88b93d51dcSIan Campbell printk(KERN_DEBUG " call %2d/%d: op=%lu arg=[%lx] result=%ld\t%pF\n", 89a122d623SJeremy Fitzhardinge i+1, b->mcidx, 90a122d623SJeremy Fitzhardinge b->debug[i].op, 91a122d623SJeremy Fitzhardinge b->debug[i].args[0], 92b93d51dcSIan Campbell b->entries[i].result, 93b93d51dcSIan Campbell b->caller[i]); 94a122d623SJeremy Fitzhardinge } 95a122d623SJeremy Fitzhardinge } 96a122d623SJeremy Fitzhardinge #endif 97a122d623SJeremy Fitzhardinge 989702785aSThomas Gleixner b->mcidx = 0; 999702785aSThomas Gleixner b->argidx = 0; 1009702785aSThomas Gleixner } else 1019702785aSThomas Gleixner BUG_ON(b->argidx != 0); 1029702785aSThomas Gleixner 10391e0c5f3SJeremy Fitzhardinge for (i = 0; i < b->cbidx; i++) { 10491e0c5f3SJeremy Fitzhardinge struct callback *cb = &b->callbacks[i]; 10591e0c5f3SJeremy Fitzhardinge 10691e0c5f3SJeremy Fitzhardinge (*cb->fn)(cb->data); 10791e0c5f3SJeremy Fitzhardinge } 10891e0c5f3SJeremy Fitzhardinge b->cbidx = 0; 10991e0c5f3SJeremy Fitzhardinge 110c9960863SJeremy Fitzhardinge local_irq_restore(flags); 111c9960863SJeremy Fitzhardinge 1123d39e9d0SJeremy Fitzhardinge WARN_ON(ret); 1139702785aSThomas Gleixner } 1149702785aSThomas Gleixner 1159702785aSThomas Gleixner struct multicall_space __xen_mc_entry(size_t args) 1169702785aSThomas Gleixner { 1179702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 1189702785aSThomas Gleixner struct multicall_space ret; 119400d3494SJeremy Fitzhardinge unsigned argidx = roundup(b->argidx, sizeof(u64)); 1209702785aSThomas Gleixner 121*c796f213SJeremy Fitzhardinge trace_xen_mc_entry_alloc(args); 122*c796f213SJeremy Fitzhardinge 1239702785aSThomas Gleixner BUG_ON(preemptible()); 124f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 1259702785aSThomas Gleixner 1269702785aSThomas Gleixner if (b->mcidx == MC_BATCH || 127f124c6aeSDan Carpenter (argidx + args) >= MC_ARGS) { 128*c796f213SJeremy Fitzhardinge trace_xen_mc_flush_reason((b->mcidx == MC_BATCH) ? 129*c796f213SJeremy Fitzhardinge XEN_MC_FL_BATCH : XEN_MC_FL_ARGS); 1309702785aSThomas Gleixner xen_mc_flush(); 131400d3494SJeremy Fitzhardinge argidx = roundup(b->argidx, sizeof(u64)); 132400d3494SJeremy Fitzhardinge } 1339702785aSThomas Gleixner 1349702785aSThomas Gleixner ret.mc = &b->entries[b->mcidx]; 135b93d51dcSIan Campbell #ifdef MC_DEBUG 136b93d51dcSIan Campbell b->caller[b->mcidx] = __builtin_return_address(0); 137b93d51dcSIan Campbell #endif 1389702785aSThomas Gleixner b->mcidx++; 139400d3494SJeremy Fitzhardinge ret.args = &b->args[argidx]; 140400d3494SJeremy Fitzhardinge b->argidx = argidx + args; 1419702785aSThomas Gleixner 142f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 143400d3494SJeremy Fitzhardinge return ret; 144400d3494SJeremy Fitzhardinge } 145400d3494SJeremy Fitzhardinge 146400d3494SJeremy Fitzhardinge struct multicall_space xen_mc_extend_args(unsigned long op, size_t size) 147400d3494SJeremy Fitzhardinge { 148400d3494SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 149400d3494SJeremy Fitzhardinge struct multicall_space ret = { NULL, NULL }; 150400d3494SJeremy Fitzhardinge 151400d3494SJeremy Fitzhardinge BUG_ON(preemptible()); 152f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 153400d3494SJeremy Fitzhardinge 154*c796f213SJeremy Fitzhardinge if (unlikely(b->mcidx == 0 || 155*c796f213SJeremy Fitzhardinge b->entries[b->mcidx - 1].op != op)) { 156*c796f213SJeremy Fitzhardinge trace_xen_mc_extend_args(op, size, XEN_MC_XE_BAD_OP); 157*c796f213SJeremy Fitzhardinge goto out; 158*c796f213SJeremy Fitzhardinge } 159400d3494SJeremy Fitzhardinge 160*c796f213SJeremy Fitzhardinge if (unlikely((b->argidx + size) >= MC_ARGS)) { 161*c796f213SJeremy Fitzhardinge trace_xen_mc_extend_args(op, size, XEN_MC_XE_NO_SPACE); 162*c796f213SJeremy Fitzhardinge goto out; 163*c796f213SJeremy Fitzhardinge } 164400d3494SJeremy Fitzhardinge 165400d3494SJeremy Fitzhardinge ret.mc = &b->entries[b->mcidx - 1]; 166400d3494SJeremy Fitzhardinge ret.args = &b->args[b->argidx]; 167400d3494SJeremy Fitzhardinge b->argidx += size; 168400d3494SJeremy Fitzhardinge 169f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 170*c796f213SJeremy Fitzhardinge 171*c796f213SJeremy Fitzhardinge trace_xen_mc_extend_args(op, size, XEN_MC_XE_OK); 172*c796f213SJeremy Fitzhardinge out: 1739702785aSThomas Gleixner return ret; 1749702785aSThomas Gleixner } 17591e0c5f3SJeremy Fitzhardinge 17691e0c5f3SJeremy Fitzhardinge void xen_mc_callback(void (*fn)(void *), void *data) 17791e0c5f3SJeremy Fitzhardinge { 17891e0c5f3SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 17991e0c5f3SJeremy Fitzhardinge struct callback *cb; 18091e0c5f3SJeremy Fitzhardinge 181*c796f213SJeremy Fitzhardinge if (b->cbidx == MC_BATCH) { 182*c796f213SJeremy Fitzhardinge trace_xen_mc_flush_reason(XEN_MC_FL_CALLBACK); 18391e0c5f3SJeremy Fitzhardinge xen_mc_flush(); 184*c796f213SJeremy Fitzhardinge } 185*c796f213SJeremy Fitzhardinge 186*c796f213SJeremy Fitzhardinge trace_xen_mc_callback(fn, data); 18791e0c5f3SJeremy Fitzhardinge 18891e0c5f3SJeremy Fitzhardinge cb = &b->callbacks[b->cbidx++]; 18991e0c5f3SJeremy Fitzhardinge cb->fn = fn; 19091e0c5f3SJeremy Fitzhardinge cb->data = data; 19191e0c5f3SJeremy Fitzhardinge } 192