Skip to content

Commit 13acfec

Browse files
committed
RISC-V: KVM: Add remote HFENCE functions based on VCPU requests
The generic KVM has support for VCPU requests which can be used to do arch-specific work in the run-loop. We introduce remote HFENCE functions which will internally use VCPU requests instead of host SBI calls. Advantages of doing remote HFENCEs as VCPU requests are: 1) Multiple VCPUs of a Guest may be running on different Host CPUs so it is not always possible to determine the Host CPU mask for doing Host SBI call. For example, when VCPU X wants to do HFENCE on VCPU Y, it is possible that VCPU Y is blocked or in user-space (i.e. vcpu->cpu < 0). 2) To support nested virtualization, we will be having a separate shadow G-stage for each VCPU and a common host G-stage for the entire Guest/VM. The VCPU requests based remote HFENCEs helps us easily synchronize the common host G-stage and shadow G-stage of each VCPU without any additional IPI calls. This is also a preparatory patch for upcoming nested virtualization support where we will be having a shadow G-stage page table for each Guest VCPU. Signed-off-by: Anup Patel <[email protected]> Reviewed-by: Atish Patra <[email protected]> Signed-off-by: Anup Patel <[email protected]>
1 parent 486a384 commit 13acfec

File tree

7 files changed

+369
-53
lines changed

7 files changed

+369
-53
lines changed

arch/riscv/include/asm/kvm_host.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/types.h>
1313
#include <linux/kvm.h>
1414
#include <linux/kvm_types.h>
15+
#include <linux/spinlock.h>
1516
#include <asm/csr.h>
1617
#include <asm/kvm_vcpu_fp.h>
1718
#include <asm/kvm_vcpu_timer.h>
@@ -26,6 +27,31 @@
2627
KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
2728
#define KVM_REQ_VCPU_RESET KVM_ARCH_REQ(1)
2829
#define KVM_REQ_UPDATE_HGATP KVM_ARCH_REQ(2)
30+
#define KVM_REQ_FENCE_I \
31+
KVM_ARCH_REQ_FLAGS(3, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
32+
#define KVM_REQ_HFENCE_GVMA_VMID_ALL KVM_REQ_TLB_FLUSH
33+
#define KVM_REQ_HFENCE_VVMA_ALL \
34+
KVM_ARCH_REQ_FLAGS(4, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
35+
#define KVM_REQ_HFENCE \
36+
KVM_ARCH_REQ_FLAGS(5, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
37+
38+
enum kvm_riscv_hfence_type {
39+
KVM_RISCV_HFENCE_UNKNOWN = 0,
40+
KVM_RISCV_HFENCE_GVMA_VMID_GPA,
41+
KVM_RISCV_HFENCE_VVMA_ASID_GVA,
42+
KVM_RISCV_HFENCE_VVMA_ASID_ALL,
43+
KVM_RISCV_HFENCE_VVMA_GVA,
44+
};
45+
46+
struct kvm_riscv_hfence {
47+
enum kvm_riscv_hfence_type type;
48+
unsigned long asid;
49+
unsigned long order;
50+
gpa_t addr;
51+
gpa_t size;
52+
};
53+
54+
#define KVM_RISCV_VCPU_MAX_HFENCE 64
2955

3056
struct kvm_vm_stat {
3157
struct kvm_vm_stat_generic generic;
@@ -178,6 +204,12 @@ struct kvm_vcpu_arch {
178204
/* VCPU Timer */
179205
struct kvm_vcpu_timer timer;
180206

207+
/* HFENCE request queue */
208+
spinlock_t hfence_lock;
209+
unsigned long hfence_head;
210+
unsigned long hfence_tail;
211+
struct kvm_riscv_hfence hfence_queue[KVM_RISCV_VCPU_MAX_HFENCE];
212+
181213
/* MMIO instruction details */
182214
struct kvm_mmio_decode mmio_decode;
183215

@@ -221,6 +253,33 @@ void kvm_riscv_local_hfence_vvma_gva(unsigned long vmid,
221253
unsigned long order);
222254
void kvm_riscv_local_hfence_vvma_all(unsigned long vmid);
223255

256+
void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu);
257+
void kvm_riscv_hfence_gvma_vmid_all_process(struct kvm_vcpu *vcpu);
258+
void kvm_riscv_hfence_vvma_all_process(struct kvm_vcpu *vcpu);
259+
void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu);
260+
261+
void kvm_riscv_fence_i(struct kvm *kvm,
262+
unsigned long hbase, unsigned long hmask);
263+
void kvm_riscv_hfence_gvma_vmid_gpa(struct kvm *kvm,
264+
unsigned long hbase, unsigned long hmask,
265+
gpa_t gpa, gpa_t gpsz,
266+
unsigned long order);
267+
void kvm_riscv_hfence_gvma_vmid_all(struct kvm *kvm,
268+
unsigned long hbase, unsigned long hmask);
269+
void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm,
270+
unsigned long hbase, unsigned long hmask,
271+
unsigned long gva, unsigned long gvsz,
272+
unsigned long order, unsigned long asid);
273+
void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm,
274+
unsigned long hbase, unsigned long hmask,
275+
unsigned long asid);
276+
void kvm_riscv_hfence_vvma_gva(struct kvm *kvm,
277+
unsigned long hbase, unsigned long hmask,
278+
unsigned long gva, unsigned long gvsz,
279+
unsigned long order);
280+
void kvm_riscv_hfence_vvma_all(struct kvm *kvm,
281+
unsigned long hbase, unsigned long hmask);
282+
224283
int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu,
225284
struct kvm_memory_slot *memslot,
226285
gpa_t gpa, unsigned long hva, bool is_write);

arch/riscv/kvm/mmu.c

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include <asm/csr.h>
1919
#include <asm/page.h>
2020
#include <asm/pgtable.h>
21-
#include <asm/sbi.h>
2221

2322
#ifdef CONFIG_64BIT
2423
static unsigned long gstage_mode = (HGATP_MODE_SV39X4 << HGATP_MODE_SHIFT);
@@ -73,13 +72,25 @@ static int gstage_page_size_to_level(unsigned long page_size, u32 *out_level)
7372
return -EINVAL;
7473
}
7574

76-
static int gstage_level_to_page_size(u32 level, unsigned long *out_pgsize)
75+
static int gstage_level_to_page_order(u32 level, unsigned long *out_pgorder)
7776
{
7877
if (gstage_pgd_levels < level)
7978
return -EINVAL;
8079

81-
*out_pgsize = 1UL << (12 + (level * gstage_index_bits));
80+
*out_pgorder = 12 + (level * gstage_index_bits);
81+
return 0;
82+
}
8283

84+
static int gstage_level_to_page_size(u32 level, unsigned long *out_pgsize)
85+
{
86+
int rc;
87+
unsigned long page_order = PAGE_SHIFT;
88+
89+
rc = gstage_level_to_page_order(level, &page_order);
90+
if (rc)
91+
return rc;
92+
93+
*out_pgsize = BIT(page_order);
8394
return 0;
8495
}
8596

@@ -114,21 +125,13 @@ static bool gstage_get_leaf_entry(struct kvm *kvm, gpa_t addr,
114125

115126
static void gstage_remote_tlb_flush(struct kvm *kvm, u32 level, gpa_t addr)
116127
{
117-
unsigned long size = PAGE_SIZE;
118-
struct kvm_vmid *vmid = &kvm->arch.vmid;
128+
unsigned long order = PAGE_SHIFT;
119129

120-
if (gstage_level_to_page_size(level, &size))
130+
if (gstage_level_to_page_order(level, &order))
121131
return;
122-
addr &= ~(size - 1);
132+
addr &= ~(BIT(order) - 1);
123133

124-
/*
125-
* TODO: Instead of cpu_online_mask, we should only target CPUs
126-
* where the Guest/VM is running.
127-
*/
128-
preempt_disable();
129-
sbi_remote_hfence_gvma_vmid(cpu_online_mask, addr, size,
130-
READ_ONCE(vmid->vmid));
131-
preempt_enable();
134+
kvm_riscv_hfence_gvma_vmid_gpa(kvm, -1UL, 0, addr, BIT(order), order);
132135
}
133136

134137
static int gstage_set_pte(struct kvm *kvm, u32 level,

arch/riscv/kvm/tlb.c

Lines changed: 226 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
* Copyright (c) 2022 Ventana Micro Systems Inc.
44
*/
55

6-
#include <linux/bitops.h>
6+
#include <linux/bitmap.h>
7+
#include <linux/cpumask.h>
78
#include <linux/errno.h>
89
#include <linux/err.h>
910
#include <linux/module.h>
11+
#include <linux/smp.h>
1012
#include <linux/kvm_host.h>
13+
#include <asm/cacheflush.h>
1114
#include <asm/csr.h>
1215

1316
/*
@@ -211,3 +214,225 @@ void kvm_riscv_local_hfence_vvma_all(unsigned long vmid)
211214

212215
csr_write(CSR_HGATP, hgatp);
213216
}
217+
218+
void kvm_riscv_fence_i_process(struct kvm_vcpu *vcpu)
219+
{
220+
local_flush_icache_all();
221+
}
222+
223+
void kvm_riscv_hfence_gvma_vmid_all_process(struct kvm_vcpu *vcpu)
224+
{
225+
struct kvm_vmid *vmid;
226+
227+
vmid = &vcpu->kvm->arch.vmid;
228+
kvm_riscv_local_hfence_gvma_vmid_all(READ_ONCE(vmid->vmid));
229+
}
230+
231+
void kvm_riscv_hfence_vvma_all_process(struct kvm_vcpu *vcpu)
232+
{
233+
struct kvm_vmid *vmid;
234+
235+
vmid = &vcpu->kvm->arch.vmid;
236+
kvm_riscv_local_hfence_vvma_all(READ_ONCE(vmid->vmid));
237+
}
238+
239+
static bool vcpu_hfence_dequeue(struct kvm_vcpu *vcpu,
240+
struct kvm_riscv_hfence *out_data)
241+
{
242+
bool ret = false;
243+
struct kvm_vcpu_arch *varch = &vcpu->arch;
244+
245+
spin_lock(&varch->hfence_lock);
246+
247+
if (varch->hfence_queue[varch->hfence_head].type) {
248+
memcpy(out_data, &varch->hfence_queue[varch->hfence_head],
249+
sizeof(*out_data));
250+
varch->hfence_queue[varch->hfence_head].type = 0;
251+
252+
varch->hfence_head++;
253+
if (varch->hfence_head == KVM_RISCV_VCPU_MAX_HFENCE)
254+
varch->hfence_head = 0;
255+
256+
ret = true;
257+
}
258+
259+
spin_unlock(&varch->hfence_lock);
260+
261+
return ret;
262+
}
263+
264+
static bool vcpu_hfence_enqueue(struct kvm_vcpu *vcpu,
265+
const struct kvm_riscv_hfence *data)
266+
{
267+
bool ret = false;
268+
struct kvm_vcpu_arch *varch = &vcpu->arch;
269+
270+
spin_lock(&varch->hfence_lock);
271+
272+
if (!varch->hfence_queue[varch->hfence_tail].type) {
273+
memcpy(&varch->hfence_queue[varch->hfence_tail],
274+
data, sizeof(*data));
275+
276+
varch->hfence_tail++;
277+
if (varch->hfence_tail == KVM_RISCV_VCPU_MAX_HFENCE)
278+
varch->hfence_tail = 0;
279+
280+
ret = true;
281+
}
282+
283+
spin_unlock(&varch->hfence_lock);
284+
285+
return ret;
286+
}
287+
288+
void kvm_riscv_hfence_process(struct kvm_vcpu *vcpu)
289+
{
290+
struct kvm_riscv_hfence d = { 0 };
291+
struct kvm_vmid *v = &vcpu->kvm->arch.vmid;
292+
293+
while (vcpu_hfence_dequeue(vcpu, &d)) {
294+
switch (d.type) {
295+
case KVM_RISCV_HFENCE_UNKNOWN:
296+
break;
297+
case KVM_RISCV_HFENCE_GVMA_VMID_GPA:
298+
kvm_riscv_local_hfence_gvma_vmid_gpa(
299+
READ_ONCE(v->vmid),
300+
d.addr, d.size, d.order);
301+
break;
302+
case KVM_RISCV_HFENCE_VVMA_ASID_GVA:
303+
kvm_riscv_local_hfence_vvma_asid_gva(
304+
READ_ONCE(v->vmid), d.asid,
305+
d.addr, d.size, d.order);
306+
break;
307+
case KVM_RISCV_HFENCE_VVMA_ASID_ALL:
308+
kvm_riscv_local_hfence_vvma_asid_all(
309+
READ_ONCE(v->vmid), d.asid);
310+
break;
311+
case KVM_RISCV_HFENCE_VVMA_GVA:
312+
kvm_riscv_local_hfence_vvma_gva(
313+
READ_ONCE(v->vmid),
314+
d.addr, d.size, d.order);
315+
break;
316+
default:
317+
break;
318+
}
319+
}
320+
}
321+
322+
static void make_xfence_request(struct kvm *kvm,
323+
unsigned long hbase, unsigned long hmask,
324+
unsigned int req, unsigned int fallback_req,
325+
const struct kvm_riscv_hfence *data)
326+
{
327+
unsigned long i;
328+
struct kvm_vcpu *vcpu;
329+
unsigned int actual_req = req;
330+
DECLARE_BITMAP(vcpu_mask, KVM_MAX_VCPUS);
331+
332+
bitmap_clear(vcpu_mask, 0, KVM_MAX_VCPUS);
333+
kvm_for_each_vcpu(i, vcpu, kvm) {
334+
if (hbase != -1UL) {
335+
if (vcpu->vcpu_id < hbase)
336+
continue;
337+
if (!(hmask & (1UL << (vcpu->vcpu_id - hbase))))
338+
continue;
339+
}
340+
341+
bitmap_set(vcpu_mask, i, 1);
342+
343+
if (!data || !data->type)
344+
continue;
345+
346+
/*
347+
* Enqueue hfence data to VCPU hfence queue. If we don't
348+
* have space in the VCPU hfence queue then fallback to
349+
* a more conservative hfence request.
350+
*/
351+
if (!vcpu_hfence_enqueue(vcpu, data))
352+
actual_req = fallback_req;
353+
}
354+
355+
kvm_make_vcpus_request_mask(kvm, actual_req, vcpu_mask);
356+
}
357+
358+
void kvm_riscv_fence_i(struct kvm *kvm,
359+
unsigned long hbase, unsigned long hmask)
360+
{
361+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_FENCE_I,
362+
KVM_REQ_FENCE_I, NULL);
363+
}
364+
365+
void kvm_riscv_hfence_gvma_vmid_gpa(struct kvm *kvm,
366+
unsigned long hbase, unsigned long hmask,
367+
gpa_t gpa, gpa_t gpsz,
368+
unsigned long order)
369+
{
370+
struct kvm_riscv_hfence data;
371+
372+
data.type = KVM_RISCV_HFENCE_GVMA_VMID_GPA;
373+
data.asid = 0;
374+
data.addr = gpa;
375+
data.size = gpsz;
376+
data.order = order;
377+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE,
378+
KVM_REQ_HFENCE_GVMA_VMID_ALL, &data);
379+
}
380+
381+
void kvm_riscv_hfence_gvma_vmid_all(struct kvm *kvm,
382+
unsigned long hbase, unsigned long hmask)
383+
{
384+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE_GVMA_VMID_ALL,
385+
KVM_REQ_HFENCE_GVMA_VMID_ALL, NULL);
386+
}
387+
388+
void kvm_riscv_hfence_vvma_asid_gva(struct kvm *kvm,
389+
unsigned long hbase, unsigned long hmask,
390+
unsigned long gva, unsigned long gvsz,
391+
unsigned long order, unsigned long asid)
392+
{
393+
struct kvm_riscv_hfence data;
394+
395+
data.type = KVM_RISCV_HFENCE_VVMA_ASID_GVA;
396+
data.asid = asid;
397+
data.addr = gva;
398+
data.size = gvsz;
399+
data.order = order;
400+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE,
401+
KVM_REQ_HFENCE_VVMA_ALL, &data);
402+
}
403+
404+
void kvm_riscv_hfence_vvma_asid_all(struct kvm *kvm,
405+
unsigned long hbase, unsigned long hmask,
406+
unsigned long asid)
407+
{
408+
struct kvm_riscv_hfence data;
409+
410+
data.type = KVM_RISCV_HFENCE_VVMA_ASID_ALL;
411+
data.asid = asid;
412+
data.addr = data.size = data.order = 0;
413+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE,
414+
KVM_REQ_HFENCE_VVMA_ALL, &data);
415+
}
416+
417+
void kvm_riscv_hfence_vvma_gva(struct kvm *kvm,
418+
unsigned long hbase, unsigned long hmask,
419+
unsigned long gva, unsigned long gvsz,
420+
unsigned long order)
421+
{
422+
struct kvm_riscv_hfence data;
423+
424+
data.type = KVM_RISCV_HFENCE_VVMA_GVA;
425+
data.asid = 0;
426+
data.addr = gva;
427+
data.size = gvsz;
428+
data.order = order;
429+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE,
430+
KVM_REQ_HFENCE_VVMA_ALL, &data);
431+
}
432+
433+
void kvm_riscv_hfence_vvma_all(struct kvm *kvm,
434+
unsigned long hbase, unsigned long hmask)
435+
{
436+
make_xfence_request(kvm, hbase, hmask, KVM_REQ_HFENCE_VVMA_ALL,
437+
KVM_REQ_HFENCE_VVMA_ALL, NULL);
438+
}

0 commit comments

Comments
 (0)