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 33ffc78767SJeremy Fitzhardinge #define MC_DEBUG 0 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); 58*eac303bfSJeremy Fitzhardinge struct multicall_entry *mc; 599702785aSThomas Gleixner int ret = 0; 609702785aSThomas Gleixner unsigned long flags; 6191e0c5f3SJeremy Fitzhardinge int i; 629702785aSThomas Gleixner 639702785aSThomas Gleixner BUG_ON(preemptible()); 649702785aSThomas Gleixner 659702785aSThomas Gleixner /* Disable interrupts in case someone comes in and queues 669702785aSThomas Gleixner something in the middle */ 679702785aSThomas Gleixner local_irq_save(flags); 689702785aSThomas Gleixner 69c796f213SJeremy Fitzhardinge trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx); 70c796f213SJeremy Fitzhardinge 71*eac303bfSJeremy Fitzhardinge switch (b->mcidx) { 72*eac303bfSJeremy Fitzhardinge case 0: 73*eac303bfSJeremy Fitzhardinge /* no-op */ 74*eac303bfSJeremy Fitzhardinge BUG_ON(b->argidx != 0); 75*eac303bfSJeremy Fitzhardinge break; 76*eac303bfSJeremy Fitzhardinge 77*eac303bfSJeremy Fitzhardinge case 1: 78*eac303bfSJeremy Fitzhardinge /* Singleton multicall - bypass multicall machinery 79*eac303bfSJeremy Fitzhardinge and just do the call directly. */ 80*eac303bfSJeremy Fitzhardinge mc = &b->entries[0]; 81*eac303bfSJeremy Fitzhardinge 82*eac303bfSJeremy Fitzhardinge mc->result = privcmd_call(mc->op, 83*eac303bfSJeremy Fitzhardinge mc->args[0], mc->args[1], mc->args[2], 84*eac303bfSJeremy Fitzhardinge mc->args[3], mc->args[4]); 85*eac303bfSJeremy Fitzhardinge ret = mc->result < 0; 86*eac303bfSJeremy Fitzhardinge break; 87*eac303bfSJeremy Fitzhardinge 88*eac303bfSJeremy Fitzhardinge default: 89a122d623SJeremy Fitzhardinge #if MC_DEBUG 90a122d623SJeremy Fitzhardinge memcpy(b->debug, b->entries, 91a122d623SJeremy Fitzhardinge b->mcidx * sizeof(struct multicall_entry)); 92a122d623SJeremy Fitzhardinge #endif 93a122d623SJeremy Fitzhardinge 949702785aSThomas Gleixner if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) 959702785aSThomas Gleixner BUG(); 969702785aSThomas Gleixner for (i = 0; i < b->mcidx; i++) 979702785aSThomas Gleixner if (b->entries[i].result < 0) 989702785aSThomas Gleixner ret++; 99a122d623SJeremy Fitzhardinge 100a122d623SJeremy Fitzhardinge #if MC_DEBUG 101a122d623SJeremy Fitzhardinge if (ret) { 102a122d623SJeremy Fitzhardinge printk(KERN_ERR "%d multicall(s) failed: cpu %d\n", 103a122d623SJeremy Fitzhardinge ret, smp_processor_id()); 1048ba6c2b0SJeremy Fitzhardinge dump_stack(); 105a122d623SJeremy Fitzhardinge for (i = 0; i < b->mcidx; i++) { 106b93d51dcSIan Campbell printk(KERN_DEBUG " call %2d/%d: op=%lu arg=[%lx] result=%ld\t%pF\n", 107a122d623SJeremy Fitzhardinge i+1, b->mcidx, 108a122d623SJeremy Fitzhardinge b->debug[i].op, 109a122d623SJeremy Fitzhardinge b->debug[i].args[0], 110b93d51dcSIan Campbell b->entries[i].result, 111b93d51dcSIan Campbell b->caller[i]); 112a122d623SJeremy Fitzhardinge } 113a122d623SJeremy Fitzhardinge } 114a122d623SJeremy Fitzhardinge #endif 115*eac303bfSJeremy Fitzhardinge } 116a122d623SJeremy Fitzhardinge 1179702785aSThomas Gleixner b->mcidx = 0; 1189702785aSThomas Gleixner b->argidx = 0; 1199702785aSThomas Gleixner 12091e0c5f3SJeremy Fitzhardinge for (i = 0; i < b->cbidx; i++) { 12191e0c5f3SJeremy Fitzhardinge struct callback *cb = &b->callbacks[i]; 12291e0c5f3SJeremy Fitzhardinge 12391e0c5f3SJeremy Fitzhardinge (*cb->fn)(cb->data); 12491e0c5f3SJeremy Fitzhardinge } 12591e0c5f3SJeremy Fitzhardinge b->cbidx = 0; 12691e0c5f3SJeremy Fitzhardinge 127c9960863SJeremy Fitzhardinge local_irq_restore(flags); 128c9960863SJeremy Fitzhardinge 1293d39e9d0SJeremy Fitzhardinge WARN_ON(ret); 1309702785aSThomas Gleixner } 1319702785aSThomas Gleixner 1329702785aSThomas Gleixner struct multicall_space __xen_mc_entry(size_t args) 1339702785aSThomas Gleixner { 1349702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 1359702785aSThomas Gleixner struct multicall_space ret; 136400d3494SJeremy Fitzhardinge unsigned argidx = roundup(b->argidx, sizeof(u64)); 1379702785aSThomas Gleixner 138c796f213SJeremy Fitzhardinge trace_xen_mc_entry_alloc(args); 139c796f213SJeremy Fitzhardinge 1409702785aSThomas Gleixner BUG_ON(preemptible()); 141f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 1429702785aSThomas Gleixner 1434a7b005dSJeremy Fitzhardinge if (unlikely(b->mcidx == MC_BATCH || 1444a7b005dSJeremy Fitzhardinge (argidx + args) >= MC_ARGS)) { 145c796f213SJeremy Fitzhardinge trace_xen_mc_flush_reason((b->mcidx == MC_BATCH) ? 146c796f213SJeremy Fitzhardinge XEN_MC_FL_BATCH : XEN_MC_FL_ARGS); 1479702785aSThomas Gleixner xen_mc_flush(); 148400d3494SJeremy Fitzhardinge argidx = roundup(b->argidx, sizeof(u64)); 149400d3494SJeremy Fitzhardinge } 1509702785aSThomas Gleixner 1519702785aSThomas Gleixner ret.mc = &b->entries[b->mcidx]; 152ffc78767SJeremy Fitzhardinge #if MC_DEBUG 153b93d51dcSIan Campbell b->caller[b->mcidx] = __builtin_return_address(0); 154b93d51dcSIan Campbell #endif 1559702785aSThomas Gleixner b->mcidx++; 156400d3494SJeremy Fitzhardinge ret.args = &b->args[argidx]; 157400d3494SJeremy Fitzhardinge b->argidx = argidx + args; 1589702785aSThomas Gleixner 159f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 160400d3494SJeremy Fitzhardinge return ret; 161400d3494SJeremy Fitzhardinge } 162400d3494SJeremy Fitzhardinge 163400d3494SJeremy Fitzhardinge struct multicall_space xen_mc_extend_args(unsigned long op, size_t size) 164400d3494SJeremy Fitzhardinge { 165400d3494SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 166400d3494SJeremy Fitzhardinge struct multicall_space ret = { NULL, NULL }; 167400d3494SJeremy Fitzhardinge 168400d3494SJeremy Fitzhardinge BUG_ON(preemptible()); 169f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 170400d3494SJeremy Fitzhardinge 171c796f213SJeremy Fitzhardinge if (unlikely(b->mcidx == 0 || 172c796f213SJeremy Fitzhardinge b->entries[b->mcidx - 1].op != op)) { 173c796f213SJeremy Fitzhardinge trace_xen_mc_extend_args(op, size, XEN_MC_XE_BAD_OP); 174c796f213SJeremy Fitzhardinge goto out; 175c796f213SJeremy Fitzhardinge } 176400d3494SJeremy Fitzhardinge 177c796f213SJeremy Fitzhardinge if (unlikely((b->argidx + size) >= MC_ARGS)) { 178c796f213SJeremy Fitzhardinge trace_xen_mc_extend_args(op, size, XEN_MC_XE_NO_SPACE); 179c796f213SJeremy Fitzhardinge goto out; 180c796f213SJeremy Fitzhardinge } 181400d3494SJeremy Fitzhardinge 182400d3494SJeremy Fitzhardinge ret.mc = &b->entries[b->mcidx - 1]; 183400d3494SJeremy Fitzhardinge ret.args = &b->args[b->argidx]; 184400d3494SJeremy Fitzhardinge b->argidx += size; 185400d3494SJeremy Fitzhardinge 186f124c6aeSDan Carpenter BUG_ON(b->argidx >= MC_ARGS); 187c796f213SJeremy Fitzhardinge 188c796f213SJeremy Fitzhardinge trace_xen_mc_extend_args(op, size, XEN_MC_XE_OK); 189c796f213SJeremy Fitzhardinge out: 1909702785aSThomas Gleixner return ret; 1919702785aSThomas Gleixner } 19291e0c5f3SJeremy Fitzhardinge 19391e0c5f3SJeremy Fitzhardinge void xen_mc_callback(void (*fn)(void *), void *data) 19491e0c5f3SJeremy Fitzhardinge { 19591e0c5f3SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 19691e0c5f3SJeremy Fitzhardinge struct callback *cb; 19791e0c5f3SJeremy Fitzhardinge 198c796f213SJeremy Fitzhardinge if (b->cbidx == MC_BATCH) { 199c796f213SJeremy Fitzhardinge trace_xen_mc_flush_reason(XEN_MC_FL_CALLBACK); 20091e0c5f3SJeremy Fitzhardinge xen_mc_flush(); 201c796f213SJeremy Fitzhardinge } 202c796f213SJeremy Fitzhardinge 203c796f213SJeremy Fitzhardinge trace_xen_mc_callback(fn, data); 20491e0c5f3SJeremy Fitzhardinge 20591e0c5f3SJeremy Fitzhardinge cb = &b->callbacks[b->cbidx++]; 20691e0c5f3SJeremy Fitzhardinge cb->fn = fn; 20791e0c5f3SJeremy Fitzhardinge cb->data = data; 20891e0c5f3SJeremy Fitzhardinge } 209