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> 24*994025caSJeremy Fitzhardinge #include <linux/debugfs.h> 259702785aSThomas Gleixner 269702785aSThomas Gleixner #include <asm/xen/hypercall.h> 279702785aSThomas Gleixner 289702785aSThomas Gleixner #include "multicalls.h" 29*994025caSJeremy Fitzhardinge #include "debugfs.h" 30*994025caSJeremy Fitzhardinge 31*994025caSJeremy 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 37*994025caSJeremy 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]; 42a122d623SJeremy Fitzhardinge #endif 43400d3494SJeremy Fitzhardinge unsigned char args[MC_ARGS]; 4491e0c5f3SJeremy Fitzhardinge struct callback { 4591e0c5f3SJeremy Fitzhardinge void (*fn)(void *); 4691e0c5f3SJeremy Fitzhardinge void *data; 4791e0c5f3SJeremy Fitzhardinge } callbacks[MC_BATCH]; 4891e0c5f3SJeremy Fitzhardinge unsigned mcidx, argidx, cbidx; 499702785aSThomas Gleixner }; 509702785aSThomas Gleixner 519702785aSThomas Gleixner static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); 529702785aSThomas Gleixner DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); 539702785aSThomas Gleixner 54*994025caSJeremy Fitzhardinge /* flush reasons 0- slots, 1- args, 2- callbacks */ 55*994025caSJeremy Fitzhardinge enum flush_reasons 56*994025caSJeremy Fitzhardinge { 57*994025caSJeremy Fitzhardinge FL_SLOTS, 58*994025caSJeremy Fitzhardinge FL_ARGS, 59*994025caSJeremy Fitzhardinge FL_CALLBACKS, 60*994025caSJeremy Fitzhardinge 61*994025caSJeremy Fitzhardinge FL_N_REASONS 62*994025caSJeremy Fitzhardinge }; 63*994025caSJeremy Fitzhardinge 64*994025caSJeremy Fitzhardinge #ifdef CONFIG_XEN_DEBUG_FS 65*994025caSJeremy Fitzhardinge #define NHYPERCALLS 40 /* not really */ 66*994025caSJeremy Fitzhardinge 67*994025caSJeremy Fitzhardinge static struct { 68*994025caSJeremy Fitzhardinge unsigned histo[MC_BATCH+1]; 69*994025caSJeremy Fitzhardinge 70*994025caSJeremy Fitzhardinge unsigned issued; 71*994025caSJeremy Fitzhardinge unsigned arg_total; 72*994025caSJeremy Fitzhardinge unsigned hypercalls; 73*994025caSJeremy Fitzhardinge unsigned histo_hypercalls[NHYPERCALLS]; 74*994025caSJeremy Fitzhardinge 75*994025caSJeremy Fitzhardinge unsigned flush[FL_N_REASONS]; 76*994025caSJeremy Fitzhardinge } mc_stats; 77*994025caSJeremy Fitzhardinge 78*994025caSJeremy Fitzhardinge static u8 zero_stats; 79*994025caSJeremy Fitzhardinge 80*994025caSJeremy Fitzhardinge static inline void check_zero(void) 81*994025caSJeremy Fitzhardinge { 82*994025caSJeremy Fitzhardinge if (unlikely(zero_stats)) { 83*994025caSJeremy Fitzhardinge memset(&mc_stats, 0, sizeof(mc_stats)); 84*994025caSJeremy Fitzhardinge zero_stats = 0; 85*994025caSJeremy Fitzhardinge } 86*994025caSJeremy Fitzhardinge } 87*994025caSJeremy Fitzhardinge 88*994025caSJeremy Fitzhardinge static void mc_add_stats(const struct mc_buffer *mc) 89*994025caSJeremy Fitzhardinge { 90*994025caSJeremy Fitzhardinge int i; 91*994025caSJeremy Fitzhardinge 92*994025caSJeremy Fitzhardinge check_zero(); 93*994025caSJeremy Fitzhardinge 94*994025caSJeremy Fitzhardinge mc_stats.issued++; 95*994025caSJeremy Fitzhardinge mc_stats.hypercalls += mc->mcidx; 96*994025caSJeremy Fitzhardinge mc_stats.arg_total += mc->argidx; 97*994025caSJeremy Fitzhardinge 98*994025caSJeremy Fitzhardinge mc_stats.histo[mc->mcidx]++; 99*994025caSJeremy Fitzhardinge for(i = 0; i < mc->mcidx; i++) { 100*994025caSJeremy Fitzhardinge unsigned op = mc->entries[i].op; 101*994025caSJeremy Fitzhardinge if (op < NHYPERCALLS) 102*994025caSJeremy Fitzhardinge mc_stats.histo_hypercalls[op]++; 103*994025caSJeremy Fitzhardinge } 104*994025caSJeremy Fitzhardinge } 105*994025caSJeremy Fitzhardinge 106*994025caSJeremy Fitzhardinge static void mc_stats_flush(enum flush_reasons idx) 107*994025caSJeremy Fitzhardinge { 108*994025caSJeremy Fitzhardinge check_zero(); 109*994025caSJeremy Fitzhardinge 110*994025caSJeremy Fitzhardinge mc_stats.flush[idx]++; 111*994025caSJeremy Fitzhardinge } 112*994025caSJeremy Fitzhardinge 113*994025caSJeremy Fitzhardinge #else /* !CONFIG_XEN_DEBUG_FS */ 114*994025caSJeremy Fitzhardinge 115*994025caSJeremy Fitzhardinge static inline void mc_add_stats(const struct mc_buffer *mc) 116*994025caSJeremy Fitzhardinge { 117*994025caSJeremy Fitzhardinge } 118*994025caSJeremy Fitzhardinge 119*994025caSJeremy Fitzhardinge static inline void mc_stats_flush(enum flush_reasons idx) 120*994025caSJeremy Fitzhardinge { 121*994025caSJeremy Fitzhardinge } 122*994025caSJeremy Fitzhardinge #endif /* CONFIG_XEN_DEBUG_FS */ 123*994025caSJeremy Fitzhardinge 1249702785aSThomas Gleixner void xen_mc_flush(void) 1259702785aSThomas Gleixner { 1269702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 1279702785aSThomas Gleixner int ret = 0; 1289702785aSThomas Gleixner unsigned long flags; 12991e0c5f3SJeremy Fitzhardinge int i; 1309702785aSThomas Gleixner 1319702785aSThomas Gleixner BUG_ON(preemptible()); 1329702785aSThomas Gleixner 1339702785aSThomas Gleixner /* Disable interrupts in case someone comes in and queues 1349702785aSThomas Gleixner something in the middle */ 1359702785aSThomas Gleixner local_irq_save(flags); 1369702785aSThomas Gleixner 137*994025caSJeremy Fitzhardinge mc_add_stats(b); 138*994025caSJeremy Fitzhardinge 1399702785aSThomas Gleixner if (b->mcidx) { 140a122d623SJeremy Fitzhardinge #if MC_DEBUG 141a122d623SJeremy Fitzhardinge memcpy(b->debug, b->entries, 142a122d623SJeremy Fitzhardinge b->mcidx * sizeof(struct multicall_entry)); 143a122d623SJeremy Fitzhardinge #endif 144a122d623SJeremy Fitzhardinge 1459702785aSThomas Gleixner if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) 1469702785aSThomas Gleixner BUG(); 1479702785aSThomas Gleixner for (i = 0; i < b->mcidx; i++) 1489702785aSThomas Gleixner if (b->entries[i].result < 0) 1499702785aSThomas Gleixner ret++; 150a122d623SJeremy Fitzhardinge 151a122d623SJeremy Fitzhardinge #if MC_DEBUG 152a122d623SJeremy Fitzhardinge if (ret) { 153a122d623SJeremy Fitzhardinge printk(KERN_ERR "%d multicall(s) failed: cpu %d\n", 154a122d623SJeremy Fitzhardinge ret, smp_processor_id()); 1558ba6c2b0SJeremy Fitzhardinge dump_stack(); 156a122d623SJeremy Fitzhardinge for (i = 0; i < b->mcidx; i++) { 157a122d623SJeremy Fitzhardinge printk(" call %2d/%d: op=%lu arg=[%lx] result=%ld\n", 158a122d623SJeremy Fitzhardinge i+1, b->mcidx, 159a122d623SJeremy Fitzhardinge b->debug[i].op, 160a122d623SJeremy Fitzhardinge b->debug[i].args[0], 161a122d623SJeremy Fitzhardinge b->entries[i].result); 162a122d623SJeremy Fitzhardinge } 163a122d623SJeremy Fitzhardinge } 164a122d623SJeremy Fitzhardinge #endif 165a122d623SJeremy Fitzhardinge 1669702785aSThomas Gleixner b->mcidx = 0; 1679702785aSThomas Gleixner b->argidx = 0; 1689702785aSThomas Gleixner } else 1699702785aSThomas Gleixner BUG_ON(b->argidx != 0); 1709702785aSThomas Gleixner 1719702785aSThomas Gleixner local_irq_restore(flags); 1729702785aSThomas Gleixner 17391e0c5f3SJeremy Fitzhardinge for (i = 0; i < b->cbidx; i++) { 17491e0c5f3SJeremy Fitzhardinge struct callback *cb = &b->callbacks[i]; 17591e0c5f3SJeremy Fitzhardinge 17691e0c5f3SJeremy Fitzhardinge (*cb->fn)(cb->data); 17791e0c5f3SJeremy Fitzhardinge } 17891e0c5f3SJeremy Fitzhardinge b->cbidx = 0; 17991e0c5f3SJeremy Fitzhardinge 1809702785aSThomas Gleixner BUG_ON(ret); 1819702785aSThomas Gleixner } 1829702785aSThomas Gleixner 1839702785aSThomas Gleixner struct multicall_space __xen_mc_entry(size_t args) 1849702785aSThomas Gleixner { 1859702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 1869702785aSThomas Gleixner struct multicall_space ret; 187400d3494SJeremy Fitzhardinge unsigned argidx = roundup(b->argidx, sizeof(u64)); 1889702785aSThomas Gleixner 1899702785aSThomas Gleixner BUG_ON(preemptible()); 190400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 1919702785aSThomas Gleixner 1929702785aSThomas Gleixner if (b->mcidx == MC_BATCH || 193400d3494SJeremy Fitzhardinge (argidx + args) > MC_ARGS) { 194*994025caSJeremy Fitzhardinge mc_stats_flush(b->mcidx == MC_BATCH ? FL_SLOTS : FL_ARGS); 1959702785aSThomas Gleixner xen_mc_flush(); 196400d3494SJeremy Fitzhardinge argidx = roundup(b->argidx, sizeof(u64)); 197400d3494SJeremy Fitzhardinge } 1989702785aSThomas Gleixner 1999702785aSThomas Gleixner ret.mc = &b->entries[b->mcidx]; 2009702785aSThomas Gleixner b->mcidx++; 201400d3494SJeremy Fitzhardinge ret.args = &b->args[argidx]; 202400d3494SJeremy Fitzhardinge b->argidx = argidx + args; 2039702785aSThomas Gleixner 204400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 205400d3494SJeremy Fitzhardinge return ret; 206400d3494SJeremy Fitzhardinge } 207400d3494SJeremy Fitzhardinge 208400d3494SJeremy Fitzhardinge struct multicall_space xen_mc_extend_args(unsigned long op, size_t size) 209400d3494SJeremy Fitzhardinge { 210400d3494SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 211400d3494SJeremy Fitzhardinge struct multicall_space ret = { NULL, NULL }; 212400d3494SJeremy Fitzhardinge 213400d3494SJeremy Fitzhardinge BUG_ON(preemptible()); 214400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 215400d3494SJeremy Fitzhardinge 216400d3494SJeremy Fitzhardinge if (b->mcidx == 0) 217400d3494SJeremy Fitzhardinge return ret; 218400d3494SJeremy Fitzhardinge 219400d3494SJeremy Fitzhardinge if (b->entries[b->mcidx - 1].op != op) 220400d3494SJeremy Fitzhardinge return ret; 221400d3494SJeremy Fitzhardinge 222400d3494SJeremy Fitzhardinge if ((b->argidx + size) > MC_ARGS) 223400d3494SJeremy Fitzhardinge return ret; 224400d3494SJeremy Fitzhardinge 225400d3494SJeremy Fitzhardinge ret.mc = &b->entries[b->mcidx - 1]; 226400d3494SJeremy Fitzhardinge ret.args = &b->args[b->argidx]; 227400d3494SJeremy Fitzhardinge b->argidx += size; 228400d3494SJeremy Fitzhardinge 229400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 2309702785aSThomas Gleixner return ret; 2319702785aSThomas Gleixner } 23291e0c5f3SJeremy Fitzhardinge 23391e0c5f3SJeremy Fitzhardinge void xen_mc_callback(void (*fn)(void *), void *data) 23491e0c5f3SJeremy Fitzhardinge { 23591e0c5f3SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 23691e0c5f3SJeremy Fitzhardinge struct callback *cb; 23791e0c5f3SJeremy Fitzhardinge 238*994025caSJeremy Fitzhardinge if (b->cbidx == MC_BATCH) { 239*994025caSJeremy Fitzhardinge mc_stats_flush(FL_CALLBACKS); 24091e0c5f3SJeremy Fitzhardinge xen_mc_flush(); 241*994025caSJeremy Fitzhardinge } 24291e0c5f3SJeremy Fitzhardinge 24391e0c5f3SJeremy Fitzhardinge cb = &b->callbacks[b->cbidx++]; 24491e0c5f3SJeremy Fitzhardinge cb->fn = fn; 24591e0c5f3SJeremy Fitzhardinge cb->data = data; 24691e0c5f3SJeremy Fitzhardinge } 247*994025caSJeremy Fitzhardinge 248*994025caSJeremy Fitzhardinge #ifdef CONFIG_XEN_DEBUG_FS 249*994025caSJeremy Fitzhardinge 250*994025caSJeremy Fitzhardinge static struct dentry *d_mc_debug; 251*994025caSJeremy Fitzhardinge 252*994025caSJeremy Fitzhardinge static int __init xen_mc_debugfs(void) 253*994025caSJeremy Fitzhardinge { 254*994025caSJeremy Fitzhardinge struct dentry *d_xen = xen_init_debugfs(); 255*994025caSJeremy Fitzhardinge 256*994025caSJeremy Fitzhardinge if (d_xen == NULL) 257*994025caSJeremy Fitzhardinge return -ENOMEM; 258*994025caSJeremy Fitzhardinge 259*994025caSJeremy Fitzhardinge d_mc_debug = debugfs_create_dir("multicalls", d_xen); 260*994025caSJeremy Fitzhardinge 261*994025caSJeremy Fitzhardinge debugfs_create_u8("zero_stats", 0644, d_mc_debug, &zero_stats); 262*994025caSJeremy Fitzhardinge 263*994025caSJeremy Fitzhardinge debugfs_create_u32("batches", 0444, d_mc_debug, &mc_stats.issued); 264*994025caSJeremy Fitzhardinge debugfs_create_u32("hypercalls", 0444, d_mc_debug, &mc_stats.hypercalls); 265*994025caSJeremy Fitzhardinge debugfs_create_u32("arg_total", 0444, d_mc_debug, &mc_stats.arg_total); 266*994025caSJeremy Fitzhardinge 267*994025caSJeremy Fitzhardinge xen_debugfs_create_u32_array("batch_histo", 0444, d_mc_debug, 268*994025caSJeremy Fitzhardinge mc_stats.histo, MC_BATCH); 269*994025caSJeremy Fitzhardinge xen_debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug, 270*994025caSJeremy Fitzhardinge mc_stats.histo_hypercalls, NHYPERCALLS); 271*994025caSJeremy Fitzhardinge xen_debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug, 272*994025caSJeremy Fitzhardinge mc_stats.flush, FL_N_REASONS); 273*994025caSJeremy Fitzhardinge 274*994025caSJeremy Fitzhardinge return 0; 275*994025caSJeremy Fitzhardinge } 276*994025caSJeremy Fitzhardinge fs_initcall(xen_mc_debugfs); 277*994025caSJeremy Fitzhardinge 278*994025caSJeremy Fitzhardinge #endif /* CONFIG_XEN_DEBUG_FS */ 279