162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xen hypercall batching. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Xen allows multiple hypercalls to be issued at once, using the 662306a36Sopenharmony_ci * multicall interface. This allows the cost of trapping into the 762306a36Sopenharmony_ci * hypervisor to be amortized over several calls. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This file implements a simple interface for multicalls. There's a 1062306a36Sopenharmony_ci * per-cpu buffer of outstanding multicalls. When you want to queue a 1162306a36Sopenharmony_ci * multicall for issuing, you can allocate a multicall slot for the 1262306a36Sopenharmony_ci * call and its arguments, along with storage for space which is 1362306a36Sopenharmony_ci * pointed to by the arguments (for passing pointers to structures, 1462306a36Sopenharmony_ci * etc). When the multicall is actually issued, all the space for the 1562306a36Sopenharmony_ci * commands and allocated memory is freed for reuse. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Multicalls are flushed whenever any of the buffers get full, or 1862306a36Sopenharmony_ci * when explicitly requested. There's no way to get per-multicall 1962306a36Sopenharmony_ci * return results back. It will BUG if any of the multicalls fail. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#include <linux/percpu.h> 2462306a36Sopenharmony_ci#include <linux/hardirq.h> 2562306a36Sopenharmony_ci#include <linux/debugfs.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/xen/hypercall.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "multicalls.h" 3062306a36Sopenharmony_ci#include "debugfs.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define MC_BATCH 32 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define MC_DEBUG 0 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define MC_ARGS (MC_BATCH * 16) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct mc_buffer { 4062306a36Sopenharmony_ci unsigned mcidx, argidx, cbidx; 4162306a36Sopenharmony_ci struct multicall_entry entries[MC_BATCH]; 4262306a36Sopenharmony_ci#if MC_DEBUG 4362306a36Sopenharmony_ci struct multicall_entry debug[MC_BATCH]; 4462306a36Sopenharmony_ci void *caller[MC_BATCH]; 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci unsigned char args[MC_ARGS]; 4762306a36Sopenharmony_ci struct callback { 4862306a36Sopenharmony_ci void (*fn)(void *); 4962306a36Sopenharmony_ci void *data; 5062306a36Sopenharmony_ci } callbacks[MC_BATCH]; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct mc_buffer, mc_buffer); 5462306a36Sopenharmony_ciDEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_civoid xen_mc_flush(void) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct mc_buffer *b = this_cpu_ptr(&mc_buffer); 5962306a36Sopenharmony_ci struct multicall_entry *mc; 6062306a36Sopenharmony_ci int ret = 0; 6162306a36Sopenharmony_ci unsigned long flags; 6262306a36Sopenharmony_ci int i; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci BUG_ON(preemptible()); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Disable interrupts in case someone comes in and queues 6762306a36Sopenharmony_ci something in the middle */ 6862306a36Sopenharmony_ci local_irq_save(flags); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci trace_xen_mc_flush(b->mcidx, b->argidx, b->cbidx); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#if MC_DEBUG 7362306a36Sopenharmony_ci memcpy(b->debug, b->entries, 7462306a36Sopenharmony_ci b->mcidx * sizeof(struct multicall_entry)); 7562306a36Sopenharmony_ci#endif 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci switch (b->mcidx) { 7862306a36Sopenharmony_ci case 0: 7962306a36Sopenharmony_ci /* no-op */ 8062306a36Sopenharmony_ci BUG_ON(b->argidx != 0); 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci case 1: 8462306a36Sopenharmony_ci /* Singleton multicall - bypass multicall machinery 8562306a36Sopenharmony_ci and just do the call directly. */ 8662306a36Sopenharmony_ci mc = &b->entries[0]; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci mc->result = xen_single_call(mc->op, mc->args[0], mc->args[1], 8962306a36Sopenharmony_ci mc->args[2], mc->args[3], 9062306a36Sopenharmony_ci mc->args[4]); 9162306a36Sopenharmony_ci ret = mc->result < 0; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci default: 9562306a36Sopenharmony_ci if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) 9662306a36Sopenharmony_ci BUG(); 9762306a36Sopenharmony_ci for (i = 0; i < b->mcidx; i++) 9862306a36Sopenharmony_ci if (b->entries[i].result < 0) 9962306a36Sopenharmony_ci ret++; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (WARN_ON(ret)) { 10362306a36Sopenharmony_ci pr_err("%d of %d multicall(s) failed: cpu %d\n", 10462306a36Sopenharmony_ci ret, b->mcidx, smp_processor_id()); 10562306a36Sopenharmony_ci for (i = 0; i < b->mcidx; i++) { 10662306a36Sopenharmony_ci if (b->entries[i].result < 0) { 10762306a36Sopenharmony_ci#if MC_DEBUG 10862306a36Sopenharmony_ci pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\t%pS\n", 10962306a36Sopenharmony_ci i + 1, 11062306a36Sopenharmony_ci b->debug[i].op, 11162306a36Sopenharmony_ci b->debug[i].args[0], 11262306a36Sopenharmony_ci b->entries[i].result, 11362306a36Sopenharmony_ci b->caller[i]); 11462306a36Sopenharmony_ci#else 11562306a36Sopenharmony_ci pr_err(" call %2d: op=%lu arg=[%lx] result=%ld\n", 11662306a36Sopenharmony_ci i + 1, 11762306a36Sopenharmony_ci b->entries[i].op, 11862306a36Sopenharmony_ci b->entries[i].args[0], 11962306a36Sopenharmony_ci b->entries[i].result); 12062306a36Sopenharmony_ci#endif 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci b->mcidx = 0; 12662306a36Sopenharmony_ci b->argidx = 0; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci for (i = 0; i < b->cbidx; i++) { 12962306a36Sopenharmony_ci struct callback *cb = &b->callbacks[i]; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci (*cb->fn)(cb->data); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci b->cbidx = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci local_irq_restore(flags); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistruct multicall_space __xen_mc_entry(size_t args) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct mc_buffer *b = this_cpu_ptr(&mc_buffer); 14162306a36Sopenharmony_ci struct multicall_space ret; 14262306a36Sopenharmony_ci unsigned argidx = roundup(b->argidx, sizeof(u64)); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci trace_xen_mc_entry_alloc(args); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci BUG_ON(preemptible()); 14762306a36Sopenharmony_ci BUG_ON(b->argidx >= MC_ARGS); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (unlikely(b->mcidx == MC_BATCH || 15062306a36Sopenharmony_ci (argidx + args) >= MC_ARGS)) { 15162306a36Sopenharmony_ci trace_xen_mc_flush_reason((b->mcidx == MC_BATCH) ? 15262306a36Sopenharmony_ci XEN_MC_FL_BATCH : XEN_MC_FL_ARGS); 15362306a36Sopenharmony_ci xen_mc_flush(); 15462306a36Sopenharmony_ci argidx = roundup(b->argidx, sizeof(u64)); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci ret.mc = &b->entries[b->mcidx]; 15862306a36Sopenharmony_ci#if MC_DEBUG 15962306a36Sopenharmony_ci b->caller[b->mcidx] = __builtin_return_address(0); 16062306a36Sopenharmony_ci#endif 16162306a36Sopenharmony_ci b->mcidx++; 16262306a36Sopenharmony_ci ret.args = &b->args[argidx]; 16362306a36Sopenharmony_ci b->argidx = argidx + args; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci BUG_ON(b->argidx >= MC_ARGS); 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct multicall_space xen_mc_extend_args(unsigned long op, size_t size) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct mc_buffer *b = this_cpu_ptr(&mc_buffer); 17262306a36Sopenharmony_ci struct multicall_space ret = { NULL, NULL }; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci BUG_ON(preemptible()); 17562306a36Sopenharmony_ci BUG_ON(b->argidx >= MC_ARGS); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (unlikely(b->mcidx == 0 || 17862306a36Sopenharmony_ci b->entries[b->mcidx - 1].op != op)) { 17962306a36Sopenharmony_ci trace_xen_mc_extend_args(op, size, XEN_MC_XE_BAD_OP); 18062306a36Sopenharmony_ci goto out; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (unlikely((b->argidx + size) >= MC_ARGS)) { 18462306a36Sopenharmony_ci trace_xen_mc_extend_args(op, size, XEN_MC_XE_NO_SPACE); 18562306a36Sopenharmony_ci goto out; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret.mc = &b->entries[b->mcidx - 1]; 18962306a36Sopenharmony_ci ret.args = &b->args[b->argidx]; 19062306a36Sopenharmony_ci b->argidx += size; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci BUG_ON(b->argidx >= MC_ARGS); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci trace_xen_mc_extend_args(op, size, XEN_MC_XE_OK); 19562306a36Sopenharmony_ciout: 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_civoid xen_mc_callback(void (*fn)(void *), void *data) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct mc_buffer *b = this_cpu_ptr(&mc_buffer); 20262306a36Sopenharmony_ci struct callback *cb; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (b->cbidx == MC_BATCH) { 20562306a36Sopenharmony_ci trace_xen_mc_flush_reason(XEN_MC_FL_CALLBACK); 20662306a36Sopenharmony_ci xen_mc_flush(); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci trace_xen_mc_callback(fn, data); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cb = &b->callbacks[b->cbidx++]; 21262306a36Sopenharmony_ci cb->fn = fn; 21362306a36Sopenharmony_ci cb->data = data; 21462306a36Sopenharmony_ci} 215