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]; 42*b93d51dcSIan 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 55994025caSJeremy Fitzhardinge /* flush reasons 0- slots, 1- args, 2- callbacks */ 56994025caSJeremy Fitzhardinge enum flush_reasons 57994025caSJeremy Fitzhardinge { 58994025caSJeremy Fitzhardinge FL_SLOTS, 59994025caSJeremy Fitzhardinge FL_ARGS, 60994025caSJeremy Fitzhardinge FL_CALLBACKS, 61994025caSJeremy Fitzhardinge 62994025caSJeremy Fitzhardinge FL_N_REASONS 63994025caSJeremy Fitzhardinge }; 64994025caSJeremy Fitzhardinge 65994025caSJeremy Fitzhardinge #ifdef CONFIG_XEN_DEBUG_FS 66994025caSJeremy Fitzhardinge #define NHYPERCALLS 40 /* not really */ 67994025caSJeremy Fitzhardinge 68994025caSJeremy Fitzhardinge static struct { 69994025caSJeremy Fitzhardinge unsigned histo[MC_BATCH+1]; 70994025caSJeremy Fitzhardinge 71994025caSJeremy Fitzhardinge unsigned issued; 72994025caSJeremy Fitzhardinge unsigned arg_total; 73994025caSJeremy Fitzhardinge unsigned hypercalls; 74994025caSJeremy Fitzhardinge unsigned histo_hypercalls[NHYPERCALLS]; 75994025caSJeremy Fitzhardinge 76994025caSJeremy Fitzhardinge unsigned flush[FL_N_REASONS]; 77994025caSJeremy Fitzhardinge } mc_stats; 78994025caSJeremy Fitzhardinge 79994025caSJeremy Fitzhardinge static u8 zero_stats; 80994025caSJeremy Fitzhardinge 81994025caSJeremy Fitzhardinge static inline void check_zero(void) 82994025caSJeremy Fitzhardinge { 83994025caSJeremy Fitzhardinge if (unlikely(zero_stats)) { 84994025caSJeremy Fitzhardinge memset(&mc_stats, 0, sizeof(mc_stats)); 85994025caSJeremy Fitzhardinge zero_stats = 0; 86994025caSJeremy Fitzhardinge } 87994025caSJeremy Fitzhardinge } 88994025caSJeremy Fitzhardinge 89994025caSJeremy Fitzhardinge static void mc_add_stats(const struct mc_buffer *mc) 90994025caSJeremy Fitzhardinge { 91994025caSJeremy Fitzhardinge int i; 92994025caSJeremy Fitzhardinge 93994025caSJeremy Fitzhardinge check_zero(); 94994025caSJeremy Fitzhardinge 95994025caSJeremy Fitzhardinge mc_stats.issued++; 96994025caSJeremy Fitzhardinge mc_stats.hypercalls += mc->mcidx; 97994025caSJeremy Fitzhardinge mc_stats.arg_total += mc->argidx; 98994025caSJeremy Fitzhardinge 99994025caSJeremy Fitzhardinge mc_stats.histo[mc->mcidx]++; 100994025caSJeremy Fitzhardinge for(i = 0; i < mc->mcidx; i++) { 101994025caSJeremy Fitzhardinge unsigned op = mc->entries[i].op; 102994025caSJeremy Fitzhardinge if (op < NHYPERCALLS) 103994025caSJeremy Fitzhardinge mc_stats.histo_hypercalls[op]++; 104994025caSJeremy Fitzhardinge } 105994025caSJeremy Fitzhardinge } 106994025caSJeremy Fitzhardinge 107994025caSJeremy Fitzhardinge static void mc_stats_flush(enum flush_reasons idx) 108994025caSJeremy Fitzhardinge { 109994025caSJeremy Fitzhardinge check_zero(); 110994025caSJeremy Fitzhardinge 111994025caSJeremy Fitzhardinge mc_stats.flush[idx]++; 112994025caSJeremy Fitzhardinge } 113994025caSJeremy Fitzhardinge 114994025caSJeremy Fitzhardinge #else /* !CONFIG_XEN_DEBUG_FS */ 115994025caSJeremy Fitzhardinge 116994025caSJeremy Fitzhardinge static inline void mc_add_stats(const struct mc_buffer *mc) 117994025caSJeremy Fitzhardinge { 118994025caSJeremy Fitzhardinge } 119994025caSJeremy Fitzhardinge 120994025caSJeremy Fitzhardinge static inline void mc_stats_flush(enum flush_reasons idx) 121994025caSJeremy Fitzhardinge { 122994025caSJeremy Fitzhardinge } 123994025caSJeremy Fitzhardinge #endif /* CONFIG_XEN_DEBUG_FS */ 124994025caSJeremy Fitzhardinge 1259702785aSThomas Gleixner void xen_mc_flush(void) 1269702785aSThomas Gleixner { 1279702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 1289702785aSThomas Gleixner int ret = 0; 1299702785aSThomas Gleixner unsigned long flags; 13091e0c5f3SJeremy Fitzhardinge int i; 1319702785aSThomas Gleixner 1329702785aSThomas Gleixner BUG_ON(preemptible()); 1339702785aSThomas Gleixner 1349702785aSThomas Gleixner /* Disable interrupts in case someone comes in and queues 1359702785aSThomas Gleixner something in the middle */ 1369702785aSThomas Gleixner local_irq_save(flags); 1379702785aSThomas Gleixner 138994025caSJeremy Fitzhardinge mc_add_stats(b); 139994025caSJeremy Fitzhardinge 1409702785aSThomas Gleixner if (b->mcidx) { 141a122d623SJeremy Fitzhardinge #if MC_DEBUG 142a122d623SJeremy Fitzhardinge memcpy(b->debug, b->entries, 143a122d623SJeremy Fitzhardinge b->mcidx * sizeof(struct multicall_entry)); 144a122d623SJeremy Fitzhardinge #endif 145a122d623SJeremy Fitzhardinge 1469702785aSThomas Gleixner if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) 1479702785aSThomas Gleixner BUG(); 1489702785aSThomas Gleixner for (i = 0; i < b->mcidx; i++) 1499702785aSThomas Gleixner if (b->entries[i].result < 0) 1509702785aSThomas Gleixner ret++; 151a122d623SJeremy Fitzhardinge 152a122d623SJeremy Fitzhardinge #if MC_DEBUG 153a122d623SJeremy Fitzhardinge if (ret) { 154a122d623SJeremy Fitzhardinge printk(KERN_ERR "%d multicall(s) failed: cpu %d\n", 155a122d623SJeremy Fitzhardinge ret, smp_processor_id()); 1568ba6c2b0SJeremy Fitzhardinge dump_stack(); 157a122d623SJeremy Fitzhardinge for (i = 0; i < b->mcidx; i++) { 158*b93d51dcSIan Campbell printk(KERN_DEBUG " call %2d/%d: op=%lu arg=[%lx] result=%ld\t%pF\n", 159a122d623SJeremy Fitzhardinge i+1, b->mcidx, 160a122d623SJeremy Fitzhardinge b->debug[i].op, 161a122d623SJeremy Fitzhardinge b->debug[i].args[0], 162*b93d51dcSIan Campbell b->entries[i].result, 163*b93d51dcSIan Campbell b->caller[i]); 164a122d623SJeremy Fitzhardinge } 165a122d623SJeremy Fitzhardinge } 166a122d623SJeremy Fitzhardinge #endif 167a122d623SJeremy Fitzhardinge 1689702785aSThomas Gleixner b->mcidx = 0; 1699702785aSThomas Gleixner b->argidx = 0; 1709702785aSThomas Gleixner } else 1719702785aSThomas Gleixner BUG_ON(b->argidx != 0); 1729702785aSThomas Gleixner 1739702785aSThomas Gleixner local_irq_restore(flags); 1749702785aSThomas Gleixner 17591e0c5f3SJeremy Fitzhardinge for (i = 0; i < b->cbidx; i++) { 17691e0c5f3SJeremy Fitzhardinge struct callback *cb = &b->callbacks[i]; 17791e0c5f3SJeremy Fitzhardinge 17891e0c5f3SJeremy Fitzhardinge (*cb->fn)(cb->data); 17991e0c5f3SJeremy Fitzhardinge } 18091e0c5f3SJeremy Fitzhardinge b->cbidx = 0; 18191e0c5f3SJeremy Fitzhardinge 1829702785aSThomas Gleixner BUG_ON(ret); 1839702785aSThomas Gleixner } 1849702785aSThomas Gleixner 1859702785aSThomas Gleixner struct multicall_space __xen_mc_entry(size_t args) 1869702785aSThomas Gleixner { 1879702785aSThomas Gleixner struct mc_buffer *b = &__get_cpu_var(mc_buffer); 1889702785aSThomas Gleixner struct multicall_space ret; 189400d3494SJeremy Fitzhardinge unsigned argidx = roundup(b->argidx, sizeof(u64)); 1909702785aSThomas Gleixner 1919702785aSThomas Gleixner BUG_ON(preemptible()); 192400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 1939702785aSThomas Gleixner 1949702785aSThomas Gleixner if (b->mcidx == MC_BATCH || 195400d3494SJeremy Fitzhardinge (argidx + args) > MC_ARGS) { 196994025caSJeremy Fitzhardinge mc_stats_flush(b->mcidx == MC_BATCH ? FL_SLOTS : FL_ARGS); 1979702785aSThomas Gleixner xen_mc_flush(); 198400d3494SJeremy Fitzhardinge argidx = roundup(b->argidx, sizeof(u64)); 199400d3494SJeremy Fitzhardinge } 2009702785aSThomas Gleixner 2019702785aSThomas Gleixner ret.mc = &b->entries[b->mcidx]; 202*b93d51dcSIan Campbell #ifdef MC_DEBUG 203*b93d51dcSIan Campbell b->caller[b->mcidx] = __builtin_return_address(0); 204*b93d51dcSIan Campbell #endif 2059702785aSThomas Gleixner b->mcidx++; 206400d3494SJeremy Fitzhardinge ret.args = &b->args[argidx]; 207400d3494SJeremy Fitzhardinge b->argidx = argidx + args; 2089702785aSThomas Gleixner 209400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 210400d3494SJeremy Fitzhardinge return ret; 211400d3494SJeremy Fitzhardinge } 212400d3494SJeremy Fitzhardinge 213400d3494SJeremy Fitzhardinge struct multicall_space xen_mc_extend_args(unsigned long op, size_t size) 214400d3494SJeremy Fitzhardinge { 215400d3494SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 216400d3494SJeremy Fitzhardinge struct multicall_space ret = { NULL, NULL }; 217400d3494SJeremy Fitzhardinge 218400d3494SJeremy Fitzhardinge BUG_ON(preemptible()); 219400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 220400d3494SJeremy Fitzhardinge 221400d3494SJeremy Fitzhardinge if (b->mcidx == 0) 222400d3494SJeremy Fitzhardinge return ret; 223400d3494SJeremy Fitzhardinge 224400d3494SJeremy Fitzhardinge if (b->entries[b->mcidx - 1].op != op) 225400d3494SJeremy Fitzhardinge return ret; 226400d3494SJeremy Fitzhardinge 227400d3494SJeremy Fitzhardinge if ((b->argidx + size) > MC_ARGS) 228400d3494SJeremy Fitzhardinge return ret; 229400d3494SJeremy Fitzhardinge 230400d3494SJeremy Fitzhardinge ret.mc = &b->entries[b->mcidx - 1]; 231400d3494SJeremy Fitzhardinge ret.args = &b->args[b->argidx]; 232400d3494SJeremy Fitzhardinge b->argidx += size; 233400d3494SJeremy Fitzhardinge 234400d3494SJeremy Fitzhardinge BUG_ON(b->argidx > MC_ARGS); 2359702785aSThomas Gleixner return ret; 2369702785aSThomas Gleixner } 23791e0c5f3SJeremy Fitzhardinge 23891e0c5f3SJeremy Fitzhardinge void xen_mc_callback(void (*fn)(void *), void *data) 23991e0c5f3SJeremy Fitzhardinge { 24091e0c5f3SJeremy Fitzhardinge struct mc_buffer *b = &__get_cpu_var(mc_buffer); 24191e0c5f3SJeremy Fitzhardinge struct callback *cb; 24291e0c5f3SJeremy Fitzhardinge 243994025caSJeremy Fitzhardinge if (b->cbidx == MC_BATCH) { 244994025caSJeremy Fitzhardinge mc_stats_flush(FL_CALLBACKS); 24591e0c5f3SJeremy Fitzhardinge xen_mc_flush(); 246994025caSJeremy Fitzhardinge } 24791e0c5f3SJeremy Fitzhardinge 24891e0c5f3SJeremy Fitzhardinge cb = &b->callbacks[b->cbidx++]; 24991e0c5f3SJeremy Fitzhardinge cb->fn = fn; 25091e0c5f3SJeremy Fitzhardinge cb->data = data; 25191e0c5f3SJeremy Fitzhardinge } 252994025caSJeremy Fitzhardinge 253994025caSJeremy Fitzhardinge #ifdef CONFIG_XEN_DEBUG_FS 254994025caSJeremy Fitzhardinge 255994025caSJeremy Fitzhardinge static struct dentry *d_mc_debug; 256994025caSJeremy Fitzhardinge 257994025caSJeremy Fitzhardinge static int __init xen_mc_debugfs(void) 258994025caSJeremy Fitzhardinge { 259994025caSJeremy Fitzhardinge struct dentry *d_xen = xen_init_debugfs(); 260994025caSJeremy Fitzhardinge 261994025caSJeremy Fitzhardinge if (d_xen == NULL) 262994025caSJeremy Fitzhardinge return -ENOMEM; 263994025caSJeremy Fitzhardinge 264994025caSJeremy Fitzhardinge d_mc_debug = debugfs_create_dir("multicalls", d_xen); 265994025caSJeremy Fitzhardinge 266994025caSJeremy Fitzhardinge debugfs_create_u8("zero_stats", 0644, d_mc_debug, &zero_stats); 267994025caSJeremy Fitzhardinge 268994025caSJeremy Fitzhardinge debugfs_create_u32("batches", 0444, d_mc_debug, &mc_stats.issued); 269994025caSJeremy Fitzhardinge debugfs_create_u32("hypercalls", 0444, d_mc_debug, &mc_stats.hypercalls); 270994025caSJeremy Fitzhardinge debugfs_create_u32("arg_total", 0444, d_mc_debug, &mc_stats.arg_total); 271994025caSJeremy Fitzhardinge 272994025caSJeremy Fitzhardinge xen_debugfs_create_u32_array("batch_histo", 0444, d_mc_debug, 273994025caSJeremy Fitzhardinge mc_stats.histo, MC_BATCH); 274994025caSJeremy Fitzhardinge xen_debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug, 275994025caSJeremy Fitzhardinge mc_stats.histo_hypercalls, NHYPERCALLS); 276994025caSJeremy Fitzhardinge xen_debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug, 277994025caSJeremy Fitzhardinge mc_stats.flush, FL_N_REASONS); 278994025caSJeremy Fitzhardinge 279994025caSJeremy Fitzhardinge return 0; 280994025caSJeremy Fitzhardinge } 281994025caSJeremy Fitzhardinge fs_initcall(xen_mc_debugfs); 282994025caSJeremy Fitzhardinge 283994025caSJeremy Fitzhardinge #endif /* CONFIG_XEN_DEBUG_FS */ 284