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