Skip to content

Commit 74c16b2

Browse files
bibo-maochenhuacai
authored andcommitted
LoongArch: KVM: Add PV IPI support on guest side
PARAVIRT config option and PV IPI is added for the guest side, function pv_ipi_init() is used to add IPI sending and IPI receiving hooks. This function firstly checks whether system runs in VM mode, and if kernel runs in VM mode, it will call function kvm_para_available() to detect the current hypervirsor type (now only KVM type detection is supported). The paravirt functions can work only if current hypervisor type is KVM, since there is only KVM supported on LoongArch now. PV IPI uses virtual IPI sender and virtual IPI receiver functions. With virtual IPI sender, IPI message is stored in memory rather than emulated HW. IPI multicast is also supported, and 128 vcpus can received IPIs at the same time like X86 KVM method. Hypercall method is used for IPI sending. With virtual IPI receiver, HW SWI0 is used rather than real IPI HW. Since VCPU has separate HW SWI0 like HW timer, there is no trap in IPI interrupt acknowledge. Since IPI message is stored in memory, there is no trap in getting IPI message. Signed-off-by: Bibo Mao <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent e33bda7 commit 74c16b2

File tree

8 files changed

+197
-2
lines changed

8 files changed

+197
-2
lines changed

arch/loongarch/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,15 @@ config RANDOMIZE_BASE_MAX_OFFSET
632632

633633
source "kernel/livepatch/Kconfig"
634634

635+
config PARAVIRT
636+
bool "Enable paravirtualization code"
637+
depends on AS_HAS_LVZ_EXTENSION
638+
help
639+
This changes the kernel so it can modify itself when it is run
640+
under a hypervisor, potentially improving performance significantly
641+
over full virtualization. However, when run without a hypervisor
642+
the kernel is theoretically slower and slightly larger.
643+
635644
endmenu
636645

637646
config ARCH_SELECT_MEMORY_MODEL

arch/loongarch/include/asm/hardirq.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ enum ipi_msg_type {
2222
typedef struct {
2323
unsigned int ipi_irqs[NR_IPI];
2424
unsigned int __softirq_pending;
25+
atomic_t message ____cacheline_aligned_in_smp;
2526
} ____cacheline_aligned irq_cpustat_t;
2627

2728
DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);

arch/loongarch/include/asm/paravirt.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_LOONGARCH_PARAVIRT_H
3+
#define _ASM_LOONGARCH_PARAVIRT_H
4+
5+
#ifdef CONFIG_PARAVIRT
6+
7+
#include <linux/static_call_types.h>
8+
struct static_key;
9+
extern struct static_key paravirt_steal_enabled;
10+
extern struct static_key paravirt_steal_rq_enabled;
11+
12+
u64 dummy_steal_clock(int cpu);
13+
DECLARE_STATIC_CALL(pv_steal_clock, dummy_steal_clock);
14+
15+
static inline u64 paravirt_steal_clock(int cpu)
16+
{
17+
return static_call(pv_steal_clock)(cpu);
18+
}
19+
20+
int __init pv_ipi_init(void);
21+
22+
#else
23+
24+
static inline int pv_ipi_init(void)
25+
{
26+
return 0;
27+
}
28+
29+
#endif // CONFIG_PARAVIRT
30+
#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <asm/paravirt.h>

arch/loongarch/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ obj-$(CONFIG_MODULES) += module.o module-sections.o
5151
obj-$(CONFIG_STACKTRACE) += stacktrace.o
5252

5353
obj-$(CONFIG_PROC_FS) += proc.o
54+
obj-$(CONFIG_PARAVIRT) += paravirt.o
5455

5556
obj-$(CONFIG_SMP) += smp.o
5657

arch/loongarch/kernel/irq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,5 @@ void __init init_IRQ(void)
113113
per_cpu(irq_stack, i), per_cpu(irq_stack, i) + IRQ_STACK_SIZE);
114114
}
115115

116-
set_csr_ecfg(ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | ECFGF_IPI | ECFGF_PMC);
116+
set_csr_ecfg(ECFGF_SIP0 | ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 | ECFGF_IPI | ECFGF_PMC);
117117
}

arch/loongarch/kernel/paravirt.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include <linux/export.h>
3+
#include <linux/types.h>
4+
#include <linux/interrupt.h>
5+
#include <linux/jump_label.h>
6+
#include <linux/kvm_para.h>
7+
#include <linux/static_call.h>
8+
#include <asm/paravirt.h>
9+
10+
struct static_key paravirt_steal_enabled;
11+
struct static_key paravirt_steal_rq_enabled;
12+
13+
static u64 native_steal_clock(int cpu)
14+
{
15+
return 0;
16+
}
17+
18+
DEFINE_STATIC_CALL(pv_steal_clock, native_steal_clock);
19+
20+
#ifdef CONFIG_SMP
21+
static void pv_send_ipi_single(int cpu, unsigned int action)
22+
{
23+
int min, old;
24+
irq_cpustat_t *info = &per_cpu(irq_stat, cpu);
25+
26+
old = atomic_fetch_or(BIT(action), &info->message);
27+
if (old)
28+
return;
29+
30+
min = cpu_logical_map(cpu);
31+
kvm_hypercall3(KVM_HCALL_FUNC_IPI, 1, 0, min);
32+
}
33+
34+
#define KVM_IPI_CLUSTER_SIZE (2 * BITS_PER_LONG)
35+
36+
static void pv_send_ipi_mask(const struct cpumask *mask, unsigned int action)
37+
{
38+
int i, cpu, min = 0, max = 0, old;
39+
__uint128_t bitmap = 0;
40+
irq_cpustat_t *info;
41+
42+
if (cpumask_empty(mask))
43+
return;
44+
45+
action = BIT(action);
46+
for_each_cpu(i, mask) {
47+
info = &per_cpu(irq_stat, i);
48+
old = atomic_fetch_or(action, &info->message);
49+
if (old)
50+
continue;
51+
52+
cpu = cpu_logical_map(i);
53+
if (!bitmap) {
54+
min = max = cpu;
55+
} else if (cpu < min && cpu > (max - KVM_IPI_CLUSTER_SIZE)) {
56+
/* cpu < min, and bitmap still enough */
57+
bitmap <<= min - cpu;
58+
min = cpu;
59+
} else if (cpu > min && cpu < (min + KVM_IPI_CLUSTER_SIZE)) {
60+
/* cpu > min, and bitmap still enough */
61+
max = cpu > max ? cpu : max;
62+
} else {
63+
/*
64+
* With cpu, bitmap will exceed KVM_IPI_CLUSTER_SIZE,
65+
* send IPI here directly and skip the remaining CPUs.
66+
*/
67+
kvm_hypercall3(KVM_HCALL_FUNC_IPI, (unsigned long)bitmap,
68+
(unsigned long)(bitmap >> BITS_PER_LONG), min);
69+
min = max = cpu;
70+
bitmap = 0;
71+
}
72+
__set_bit(cpu - min, (unsigned long *)&bitmap);
73+
}
74+
75+
if (bitmap)
76+
kvm_hypercall3(KVM_HCALL_FUNC_IPI, (unsigned long)bitmap,
77+
(unsigned long)(bitmap >> BITS_PER_LONG), min);
78+
}
79+
80+
static irqreturn_t pv_ipi_interrupt(int irq, void *dev)
81+
{
82+
u32 action;
83+
irq_cpustat_t *info;
84+
85+
/* Clear SWI interrupt */
86+
clear_csr_estat(1 << INT_SWI0);
87+
info = this_cpu_ptr(&irq_stat);
88+
action = atomic_xchg(&info->message, 0);
89+
90+
if (action & SMP_RESCHEDULE) {
91+
scheduler_ipi();
92+
info->ipi_irqs[IPI_RESCHEDULE]++;
93+
}
94+
95+
if (action & SMP_CALL_FUNCTION) {
96+
generic_smp_call_function_interrupt();
97+
info->ipi_irqs[IPI_CALL_FUNCTION]++;
98+
}
99+
100+
return IRQ_HANDLED;
101+
}
102+
103+
static void pv_init_ipi(void)
104+
{
105+
int r, swi;
106+
107+
swi = get_percpu_irq(INT_SWI0);
108+
if (swi < 0)
109+
panic("SWI0 IRQ mapping failed\n");
110+
irq_set_percpu_devid(swi);
111+
r = request_percpu_irq(swi, pv_ipi_interrupt, "SWI0-IPI", &irq_stat);
112+
if (r < 0)
113+
panic("SWI0 IRQ request failed\n");
114+
}
115+
#endif
116+
117+
static bool kvm_para_available(void)
118+
{
119+
int config;
120+
static int hypervisor_type;
121+
122+
if (!hypervisor_type) {
123+
config = read_cpucfg(CPUCFG_KVM_SIG);
124+
if (!memcmp(&config, KVM_SIGNATURE, 4))
125+
hypervisor_type = HYPERVISOR_KVM;
126+
}
127+
128+
return hypervisor_type == HYPERVISOR_KVM;
129+
}
130+
131+
int __init pv_ipi_init(void)
132+
{
133+
int feature;
134+
135+
if (!cpu_has_hypervisor)
136+
return 0;
137+
if (!kvm_para_available())
138+
return 0;
139+
140+
feature = read_cpucfg(CPUCFG_KVM_FEATURE);
141+
if (!(feature & KVM_FEATURE_IPI))
142+
return 0;
143+
144+
#ifdef CONFIG_SMP
145+
mp_ops.init_ipi = pv_init_ipi;
146+
mp_ops.send_ipi_single = pv_send_ipi_single;
147+
mp_ops.send_ipi_mask = pv_send_ipi_mask;
148+
#endif
149+
150+
return 0;
151+
}

arch/loongarch/kernel/smp.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <asm/loongson.h>
3030
#include <asm/mmu_context.h>
3131
#include <asm/numa.h>
32+
#include <asm/paravirt.h>
3233
#include <asm/processor.h>
3334
#include <asm/setup.h>
3435
#include <asm/time.h>
@@ -299,6 +300,7 @@ void __init loongson_smp_setup(void)
299300
cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package;
300301
cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
301302

303+
pv_ipi_init();
302304
iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
303305
pr_info("Detected %i available CPU(s)\n", loongson_sysconf.nr_cpus);
304306
}
@@ -343,7 +345,7 @@ void loongson_init_secondary(void)
343345
{
344346
unsigned int cpu = smp_processor_id();
345347
unsigned int imask = ECFGF_IP0 | ECFGF_IP1 | ECFGF_IP2 |
346-
ECFGF_IPI | ECFGF_PMC | ECFGF_TIMER;
348+
ECFGF_IPI | ECFGF_PMC | ECFGF_TIMER | ECFGF_SIP0;
347349

348350
change_csr_ecfg(ECFG0_IM, imask);
349351

0 commit comments

Comments
 (0)