Skip to content

Commit f21db30

Browse files
chenhuacaibonzini
authored andcommitted
KVM: MIPS: Add Loongson-3 Virtual IPI interrupt support
This patch add Loongson-3 Virtual IPI interrupt support in the kernel. The current implementation of IPI emulation in QEMU is based on GIC for MIPS, but Loongson-3 doesn't use GIC. Furthermore, IPI emulation in QEMU is too expensive for performance (because of too many context switches between Host and Guest). With current solution, the IPI delay may even cause RCU stall warnings in a multi-core Guest. So, we design a faster solution that emulate IPI interrupt in kernel (only used by Loongson-3 now). Reviewed-by: Aleksandar Markovic <[email protected]> Signed-off-by: Huacai Chen <[email protected]> Co-developed-by: Jiaxun Yang <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 3f51d8f commit f21db30

File tree

5 files changed

+277
-1
lines changed

5 files changed

+277
-1
lines changed

arch/mips/include/asm/kvm_host.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include <asm/inst.h>
2424
#include <asm/mipsregs.h>
2525

26+
#include <kvm/iodev.h>
27+
2628
/* MIPS KVM register ids */
2729
#define MIPS_CP0_32(_R, _S) \
2830
(KVM_REG_MIPS_CP0 | KVM_REG_SIZE_U32 | (8 * (_R) + (_S)))
@@ -183,11 +185,39 @@ struct kvm_vcpu_stat {
183185
struct kvm_arch_memory_slot {
184186
};
185187

188+
#ifdef CONFIG_CPU_LOONGSON64
189+
struct ipi_state {
190+
uint32_t status;
191+
uint32_t en;
192+
uint32_t set;
193+
uint32_t clear;
194+
uint64_t buf[4];
195+
};
196+
197+
struct loongson_kvm_ipi;
198+
199+
struct ipi_io_device {
200+
int node_id;
201+
struct loongson_kvm_ipi *ipi;
202+
struct kvm_io_device device;
203+
};
204+
205+
struct loongson_kvm_ipi {
206+
spinlock_t lock;
207+
struct kvm *kvm;
208+
struct ipi_state ipistate[16];
209+
struct ipi_io_device dev_ipi[4];
210+
};
211+
#endif
212+
186213
struct kvm_arch {
187214
/* Guest physical mm */
188215
struct mm_struct gpa_mm;
189216
/* Mask of CPUs needing GPA ASID flush */
190217
cpumask_t asid_flush_mask;
218+
#ifdef CONFIG_CPU_LOONGSON64
219+
struct loongson_kvm_ipi ipi;
220+
#endif
191221
};
192222

193223
#define N_MIPS_COPROC_REGS 32
@@ -1135,6 +1165,8 @@ extern int kvm_mips_trans_mtc0(union mips_instruction inst, u32 *opc,
11351165
/* Misc */
11361166
extern void kvm_mips_dump_stats(struct kvm_vcpu *vcpu);
11371167
extern unsigned long kvm_mips_get_ramsize(struct kvm *kvm);
1168+
extern int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
1169+
struct kvm_mips_interrupt *irq);
11381170

11391171
static inline void kvm_arch_hardware_unsetup(void) {}
11401172
static inline void kvm_arch_sync_events(struct kvm *kvm) {}

arch/mips/kvm/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ kvm-objs := $(common-objs-y) mips.o emulate.o entry.o \
1313
fpu.o
1414
kvm-objs += hypcall.o
1515
kvm-objs += mmu.o
16+
ifdef CONFIG_CPU_LOONGSON64
17+
kvm-objs += loongson_ipi.o
18+
endif
1619

1720
ifdef CONFIG_KVM_MIPS_VZ
1821
kvm-objs += vz.o

arch/mips/kvm/emulate.c

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,7 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
16001600
struct kvm_run *run,
16011601
struct kvm_vcpu *vcpu)
16021602
{
1603+
int r;
16031604
enum emulation_result er;
16041605
u32 rt;
16051606
void *data = run->mmio.data;
@@ -1666,9 +1667,18 @@ enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,
16661667
goto out_fail;
16671668
}
16681669

1669-
run->mmio.is_write = 1;
16701670
vcpu->mmio_needed = 1;
1671+
run->mmio.is_write = 1;
16711672
vcpu->mmio_is_write = 1;
1673+
1674+
r = kvm_io_bus_write(vcpu, KVM_MMIO_BUS,
1675+
run->mmio.phys_addr, run->mmio.len, data);
1676+
1677+
if (!r) {
1678+
vcpu->mmio_needed = 0;
1679+
return EMULATE_DONE;
1680+
}
1681+
16721682
return EMULATE_DO_MMIO;
16731683

16741684
out_fail:
@@ -1681,6 +1691,7 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
16811691
u32 cause, struct kvm_run *run,
16821692
struct kvm_vcpu *vcpu)
16831693
{
1694+
int r;
16841695
enum emulation_result er;
16851696
unsigned long curr_pc;
16861697
u32 op, rt;
@@ -1745,6 +1756,16 @@ enum emulation_result kvm_mips_emulate_load(union mips_instruction inst,
17451756

17461757
run->mmio.is_write = 0;
17471758
vcpu->mmio_is_write = 0;
1759+
1760+
r = kvm_io_bus_read(vcpu, KVM_MMIO_BUS,
1761+
run->mmio.phys_addr, run->mmio.len, run->mmio.data);
1762+
1763+
if (!r) {
1764+
kvm_mips_complete_mmio_load(vcpu, run);
1765+
vcpu->mmio_needed = 0;
1766+
return EMULATE_DONE;
1767+
}
1768+
17481769
return EMULATE_DO_MMIO;
17491770
}
17501771

arch/mips/kvm/loongson_ipi.c

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Loongson-3 Virtual IPI interrupt support.
4+
*
5+
* Copyright (C) 2019 Loongson Technologies, Inc. All rights reserved.
6+
*
7+
* Authors: Chen Zhu <[email protected]>
8+
* Authors: Huacai Chen <[email protected]>
9+
*/
10+
11+
#include <linux/kvm_host.h>
12+
13+
#define IPI_BASE 0x3ff01000ULL
14+
15+
#define CORE0_STATUS_OFF 0x000
16+
#define CORE0_EN_OFF 0x004
17+
#define CORE0_SET_OFF 0x008
18+
#define CORE0_CLEAR_OFF 0x00c
19+
#define CORE0_BUF_20 0x020
20+
#define CORE0_BUF_28 0x028
21+
#define CORE0_BUF_30 0x030
22+
#define CORE0_BUF_38 0x038
23+
24+
#define CORE1_STATUS_OFF 0x100
25+
#define CORE1_EN_OFF 0x104
26+
#define CORE1_SET_OFF 0x108
27+
#define CORE1_CLEAR_OFF 0x10c
28+
#define CORE1_BUF_20 0x120
29+
#define CORE1_BUF_28 0x128
30+
#define CORE1_BUF_30 0x130
31+
#define CORE1_BUF_38 0x138
32+
33+
#define CORE2_STATUS_OFF 0x200
34+
#define CORE2_EN_OFF 0x204
35+
#define CORE2_SET_OFF 0x208
36+
#define CORE2_CLEAR_OFF 0x20c
37+
#define CORE2_BUF_20 0x220
38+
#define CORE2_BUF_28 0x228
39+
#define CORE2_BUF_30 0x230
40+
#define CORE2_BUF_38 0x238
41+
42+
#define CORE3_STATUS_OFF 0x300
43+
#define CORE3_EN_OFF 0x304
44+
#define CORE3_SET_OFF 0x308
45+
#define CORE3_CLEAR_OFF 0x30c
46+
#define CORE3_BUF_20 0x320
47+
#define CORE3_BUF_28 0x328
48+
#define CORE3_BUF_30 0x330
49+
#define CORE3_BUF_38 0x338
50+
51+
static int loongson_vipi_read(struct loongson_kvm_ipi *ipi,
52+
gpa_t addr, int len, void *val)
53+
{
54+
uint32_t core = (addr >> 8) & 3;
55+
uint32_t node = (addr >> 44) & 3;
56+
uint32_t id = core + node * 4;
57+
uint64_t offset = addr & 0xff;
58+
void *pbuf;
59+
struct ipi_state *s = &(ipi->ipistate[id]);
60+
61+
BUG_ON(offset & (len - 1));
62+
63+
switch (offset) {
64+
case CORE0_STATUS_OFF:
65+
*(uint64_t *)val = s->status;
66+
break;
67+
68+
case CORE0_EN_OFF:
69+
*(uint64_t *)val = s->en;
70+
break;
71+
72+
case CORE0_SET_OFF:
73+
*(uint64_t *)val = 0;
74+
break;
75+
76+
case CORE0_CLEAR_OFF:
77+
*(uint64_t *)val = 0;
78+
break;
79+
80+
case CORE0_BUF_20 ... CORE0_BUF_38:
81+
pbuf = (void *)s->buf + (offset - 0x20);
82+
if (len == 8)
83+
*(uint64_t *)val = *(uint64_t *)pbuf;
84+
else /* Assume len == 4 */
85+
*(uint32_t *)val = *(uint32_t *)pbuf;
86+
break;
87+
88+
default:
89+
pr_notice("%s with unknown addr %llx\n", __func__, addr);
90+
break;
91+
}
92+
93+
return 0;
94+
}
95+
96+
static int loongson_vipi_write(struct loongson_kvm_ipi *ipi,
97+
gpa_t addr, int len, const void *val)
98+
{
99+
uint32_t core = (addr >> 8) & 3;
100+
uint32_t node = (addr >> 44) & 3;
101+
uint32_t id = core + node * 4;
102+
uint64_t data, offset = addr & 0xff;
103+
void *pbuf;
104+
struct kvm *kvm = ipi->kvm;
105+
struct kvm_mips_interrupt irq;
106+
struct ipi_state *s = &(ipi->ipistate[id]);
107+
108+
data = *(uint64_t *)val;
109+
BUG_ON(offset & (len - 1));
110+
111+
switch (offset) {
112+
case CORE0_STATUS_OFF:
113+
break;
114+
115+
case CORE0_EN_OFF:
116+
s->en = data;
117+
break;
118+
119+
case CORE0_SET_OFF:
120+
s->status |= data;
121+
irq.cpu = id;
122+
irq.irq = 6;
123+
kvm_vcpu_ioctl_interrupt(kvm->vcpus[id], &irq);
124+
break;
125+
126+
case CORE0_CLEAR_OFF:
127+
s->status &= ~data;
128+
if (!s->status) {
129+
irq.cpu = id;
130+
irq.irq = -6;
131+
kvm_vcpu_ioctl_interrupt(kvm->vcpus[id], &irq);
132+
}
133+
break;
134+
135+
case CORE0_BUF_20 ... CORE0_BUF_38:
136+
pbuf = (void *)s->buf + (offset - 0x20);
137+
if (len == 8)
138+
*(uint64_t *)pbuf = (uint64_t)data;
139+
else /* Assume len == 4 */
140+
*(uint32_t *)pbuf = (uint32_t)data;
141+
break;
142+
143+
default:
144+
pr_notice("%s with unknown addr %llx\n", __func__, addr);
145+
break;
146+
}
147+
148+
return 0;
149+
}
150+
151+
static int kvm_ipi_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
152+
gpa_t addr, int len, void *val)
153+
{
154+
unsigned long flags;
155+
struct loongson_kvm_ipi *ipi;
156+
struct ipi_io_device *ipi_device;
157+
158+
ipi_device = container_of(dev, struct ipi_io_device, device);
159+
ipi = ipi_device->ipi;
160+
161+
spin_lock_irqsave(&ipi->lock, flags);
162+
loongson_vipi_read(ipi, addr, len, val);
163+
spin_unlock_irqrestore(&ipi->lock, flags);
164+
165+
return 0;
166+
}
167+
168+
static int kvm_ipi_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
169+
gpa_t addr, int len, const void *val)
170+
{
171+
unsigned long flags;
172+
struct loongson_kvm_ipi *ipi;
173+
struct ipi_io_device *ipi_device;
174+
175+
ipi_device = container_of(dev, struct ipi_io_device, device);
176+
ipi = ipi_device->ipi;
177+
178+
spin_lock_irqsave(&ipi->lock, flags);
179+
loongson_vipi_write(ipi, addr, len, val);
180+
spin_unlock_irqrestore(&ipi->lock, flags);
181+
182+
return 0;
183+
}
184+
185+
static const struct kvm_io_device_ops kvm_ipi_ops = {
186+
.read = kvm_ipi_read,
187+
.write = kvm_ipi_write,
188+
};
189+
190+
void kvm_init_loongson_ipi(struct kvm *kvm)
191+
{
192+
int i;
193+
unsigned long addr;
194+
struct loongson_kvm_ipi *s;
195+
struct kvm_io_device *device;
196+
197+
s = &kvm->arch.ipi;
198+
s->kvm = kvm;
199+
spin_lock_init(&s->lock);
200+
201+
/*
202+
* Initialize IPI device
203+
*/
204+
for (i = 0; i < 4; i++) {
205+
device = &s->dev_ipi[i].device;
206+
kvm_iodevice_init(device, &kvm_ipi_ops);
207+
addr = (((unsigned long)i) << 44) + IPI_BASE;
208+
mutex_lock(&kvm->slots_lock);
209+
kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, addr, 0x400, device);
210+
mutex_unlock(&kvm->slots_lock);
211+
s->dev_ipi[i].ipi = s;
212+
s->dev_ipi[i].node_id = i;
213+
}
214+
}

arch/mips/kvm/mips.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ int kvm_arch_check_processor_compat(void *opaque)
129129
return 0;
130130
}
131131

132+
extern void kvm_init_loongson_ipi(struct kvm *kvm);
133+
132134
int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
133135
{
134136
switch (type) {
@@ -148,6 +150,10 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
148150
if (!kvm->arch.gpa_mm.pgd)
149151
return -ENOMEM;
150152

153+
#ifdef CONFIG_CPU_LOONGSON64
154+
kvm_init_loongson_ipi(kvm);
155+
#endif
156+
151157
return 0;
152158
}
153159

0 commit comments

Comments
 (0)