Skip to content

Commit 832f15f

Browse files
avpatelMarc Zyngier
authored andcommitted
RISC-V: Treat IPIs as normal Linux IRQs
Currently, the RISC-V kernel provides arch specific hooks (i.e. struct riscv_ipi_ops) to register IPI handling methods. The stats gathering of IPIs is also arch specific in the RISC-V kernel. Other architectures (such as ARM, ARM64, and MIPS) have moved away from custom arch specific IPI handling methods. Currently, these architectures have Linux irqchip drivers providing a range of Linux IRQ numbers to be used as IPIs and IPI triggering is done using generic IPI APIs. This approach allows architectures to treat IPIs as normal Linux IRQs and IPI stats gathering is done by the generic Linux IRQ subsystem. We extend the RISC-V IPI handling as-per above approach so that arch specific IPI handling methods (struct riscv_ipi_ops) can be removed and the IPI handling is done through the Linux IRQ subsystem. Signed-off-by: Anup Patel <apatel@ventanamicro.com> Acked-by: Palmer Dabbelt <palmer@rivosinc.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20230328035223.1480939-4-apatel@ventanamicro.com
1 parent 0c60a31 commit 832f15f

13 files changed

Lines changed: 283 additions & 239 deletions

File tree

arch/riscv/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ config RISCV
6363
select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO
6464
select GENERIC_IDLE_POLL_SETUP
6565
select GENERIC_IOREMAP if MMU
66+
select GENERIC_IRQ_IPI if SMP
67+
select GENERIC_IRQ_IPI_MUX if SMP
6668
select GENERIC_IRQ_MULTI_HANDLER
6769
select GENERIC_IRQ_SHOW
6870
select GENERIC_IRQ_SHOW_LEVEL

arch/riscv/include/asm/sbi.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,7 @@ long sbi_get_marchid(void);
271271
long sbi_get_mimpid(void);
272272
void sbi_set_timer(uint64_t stime_value);
273273
void sbi_shutdown(void);
274-
void sbi_clear_ipi(void);
275-
int sbi_send_ipi(const struct cpumask *cpu_mask);
274+
void sbi_send_ipi(unsigned int cpu);
276275
int sbi_remote_fence_i(const struct cpumask *cpu_mask);
277276
int sbi_remote_sfence_vma(const struct cpumask *cpu_mask,
278277
unsigned long start,
@@ -335,4 +334,10 @@ unsigned long riscv_cached_mvendorid(unsigned int cpu_id);
335334
unsigned long riscv_cached_marchid(unsigned int cpu_id);
336335
unsigned long riscv_cached_mimpid(unsigned int cpu_id);
337336

337+
#if IS_ENABLED(CONFIG_SMP) && IS_ENABLED(CONFIG_RISCV_SBI)
338+
void sbi_ipi_init(void);
339+
#else
340+
static inline void sbi_ipi_init(void) { }
341+
#endif
342+
338343
#endif /* _ASM_RISCV_SBI_H */

arch/riscv/include/asm/smp.h

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@
1515
struct seq_file;
1616
extern unsigned long boot_cpu_hartid;
1717

18-
struct riscv_ipi_ops {
19-
void (*ipi_inject)(const struct cpumask *target);
20-
void (*ipi_clear)(void);
21-
};
22-
2318
#ifdef CONFIG_SMP
2419
/*
2520
* Mapping between linux logical cpu index and hartid.
@@ -33,9 +28,6 @@ void show_ipi_stats(struct seq_file *p, int prec);
3328
/* SMP initialization hook for setup_arch */
3429
void __init setup_smp(void);
3530

36-
/* Called from C code, this handles an IPI. */
37-
void handle_IPI(struct pt_regs *regs);
38-
3931
/* Hook for the generic smp_call_function_many() routine. */
4032
void arch_send_call_function_ipi_mask(struct cpumask *mask);
4133

@@ -44,11 +36,17 @@ void arch_send_call_function_single_ipi(int cpu);
4436

4537
int riscv_hartid_to_cpuid(unsigned long hartid);
4638

47-
/* Set custom IPI operations */
48-
void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops);
39+
/* Enable IPI for CPU hotplug */
40+
void riscv_ipi_enable(void);
41+
42+
/* Disable IPI for CPU hotplug */
43+
void riscv_ipi_disable(void);
4944

50-
/* Clear IPI for current CPU */
51-
void riscv_clear_ipi(void);
45+
/* Check if IPI interrupt numbers are available */
46+
bool riscv_ipi_have_virq_range(void);
47+
48+
/* Set the IPI interrupt numbers for arch (called by irqchip drivers) */
49+
void riscv_ipi_set_virq_range(int virq, int nr);
5250

5351
/* Check other CPUs stop or not */
5452
bool smp_crash_stop_failed(void);
@@ -85,11 +83,20 @@ static inline unsigned long cpuid_to_hartid_map(int cpu)
8583
return boot_cpu_hartid;
8684
}
8785

88-
static inline void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
86+
static inline void riscv_ipi_enable(void)
8987
{
9088
}
9189

92-
static inline void riscv_clear_ipi(void)
90+
static inline void riscv_ipi_disable(void)
91+
{
92+
}
93+
94+
static inline bool riscv_ipi_have_virq_range(void)
95+
{
96+
return false;
97+
}
98+
99+
static inline void riscv_ipi_set_virq_range(int virq, int nr)
93100
{
94101
}
95102

arch/riscv/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
7474
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
7575
obj-$(CONFIG_RISCV_SBI) += sbi.o
7676
ifeq ($(CONFIG_RISCV_SBI), y)
77+
obj-$(CONFIG_SMP) += sbi-ipi.o
7778
obj-$(CONFIG_SMP) += cpu_ops_sbi.o
7879
endif
7980
obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o

arch/riscv/kernel/cpu-hotplug.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#include <asm/irq.h>
1414
#include <asm/cpu_ops.h>
1515
#include <asm/numa.h>
16-
#include <asm/sbi.h>
16+
#include <asm/smp.h>
1717

1818
bool cpu_has_hotplug(unsigned int cpu)
1919
{
@@ -43,6 +43,7 @@ int __cpu_disable(void)
4343
remove_cpu_topology(cpu);
4444
numa_remove_cpu(cpu);
4545
set_cpu_online(cpu, false);
46+
riscv_ipi_disable();
4647
irq_migrate_all_off_this_cpu();
4748

4849
return ret;

arch/riscv/kernel/irq.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#include <linux/irqdomain.h>
1111
#include <linux/module.h>
1212
#include <linux/seq_file.h>
13-
#include <asm/smp.h>
13+
#include <asm/sbi.h>
1414

1515
static struct fwnode_handle *(*__get_intc_node)(void);
1616

@@ -39,4 +39,5 @@ void __init init_IRQ(void)
3939
irqchip_init();
4040
if (!handle_arch_irq)
4141
panic("No interrupt controller found.");
42+
sbi_ipi_init();
4243
}

arch/riscv/kernel/sbi-ipi.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Multiplex several IPIs over a single HW IPI.
4+
*
5+
* Copyright (c) 2022 Ventana Micro Systems Inc.
6+
*/
7+
8+
#define pr_fmt(fmt) "riscv: " fmt
9+
#include <linux/cpu.h>
10+
#include <linux/init.h>
11+
#include <linux/irq.h>
12+
#include <linux/irqchip/chained_irq.h>
13+
#include <linux/irqdomain.h>
14+
#include <asm/sbi.h>
15+
16+
static int sbi_ipi_virq;
17+
18+
static void sbi_ipi_handle(struct irq_desc *desc)
19+
{
20+
struct irq_chip *chip = irq_desc_get_chip(desc);
21+
22+
chained_irq_enter(chip, desc);
23+
24+
csr_clear(CSR_IP, IE_SIE);
25+
ipi_mux_process();
26+
27+
chained_irq_exit(chip, desc);
28+
}
29+
30+
static int sbi_ipi_starting_cpu(unsigned int cpu)
31+
{
32+
enable_percpu_irq(sbi_ipi_virq, irq_get_trigger_type(sbi_ipi_virq));
33+
return 0;
34+
}
35+
36+
void __init sbi_ipi_init(void)
37+
{
38+
int virq;
39+
struct irq_domain *domain;
40+
41+
if (riscv_ipi_have_virq_range())
42+
return;
43+
44+
domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
45+
DOMAIN_BUS_ANY);
46+
if (!domain) {
47+
pr_err("unable to find INTC IRQ domain\n");
48+
return;
49+
}
50+
51+
sbi_ipi_virq = irq_create_mapping(domain, RV_IRQ_SOFT);
52+
if (!sbi_ipi_virq) {
53+
pr_err("unable to create INTC IRQ mapping\n");
54+
return;
55+
}
56+
57+
virq = ipi_mux_create(BITS_PER_BYTE, sbi_send_ipi);
58+
if (virq <= 0) {
59+
pr_err("unable to create muxed IPIs\n");
60+
irq_dispose_mapping(sbi_ipi_virq);
61+
return;
62+
}
63+
64+
irq_set_chained_handler(sbi_ipi_virq, sbi_ipi_handle);
65+
66+
/*
67+
* Don't disable IPI when CPU goes offline because
68+
* the masking/unmasking of virtual IPIs is done
69+
* via generic IPI-Mux
70+
*/
71+
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
72+
"irqchip/sbi-ipi:starting",
73+
sbi_ipi_starting_cpu, NULL);
74+
75+
riscv_ipi_set_virq_range(virq, BITS_PER_BYTE);
76+
pr_info("providing IPIs using SBI IPI extension\n");
77+
}

arch/riscv/kernel/sbi.c

Lines changed: 16 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ unsigned long sbi_spec_version __ro_after_init = SBI_SPEC_VERSION_DEFAULT;
1717
EXPORT_SYMBOL(sbi_spec_version);
1818

1919
static void (*__sbi_set_timer)(uint64_t stime) __ro_after_init;
20-
static int (*__sbi_send_ipi)(const struct cpumask *cpu_mask) __ro_after_init;
20+
static void (*__sbi_send_ipi)(unsigned int cpu) __ro_after_init;
2121
static int (*__sbi_rfence)(int fid, const struct cpumask *cpu_mask,
2222
unsigned long start, unsigned long size,
2323
unsigned long arg4, unsigned long arg5) __ro_after_init;
@@ -130,17 +130,6 @@ void sbi_shutdown(void)
130130
}
131131
EXPORT_SYMBOL(sbi_shutdown);
132132

133-
/**
134-
* sbi_clear_ipi() - Clear any pending IPIs for the calling hart.
135-
*
136-
* Return: None
137-
*/
138-
void sbi_clear_ipi(void)
139-
{
140-
sbi_ecall(SBI_EXT_0_1_CLEAR_IPI, 0, 0, 0, 0, 0, 0, 0);
141-
}
142-
EXPORT_SYMBOL(sbi_clear_ipi);
143-
144133
/**
145134
* __sbi_set_timer_v01() - Program the timer for next timer event.
146135
* @stime_value: The value after which next timer event should fire.
@@ -157,17 +146,12 @@ static void __sbi_set_timer_v01(uint64_t stime_value)
157146
#endif
158147
}
159148

160-
static int __sbi_send_ipi_v01(const struct cpumask *cpu_mask)
149+
static void __sbi_send_ipi_v01(unsigned int cpu)
161150
{
162-
unsigned long hart_mask;
163-
164-
if (!cpu_mask || cpumask_empty(cpu_mask))
165-
cpu_mask = cpu_online_mask;
166-
hart_mask = __sbi_v01_cpumask_to_hartmask(cpu_mask);
167-
151+
unsigned long hart_mask =
152+
__sbi_v01_cpumask_to_hartmask(cpumask_of(cpu));
168153
sbi_ecall(SBI_EXT_0_1_SEND_IPI, 0, (unsigned long)(&hart_mask),
169154
0, 0, 0, 0, 0);
170-
return 0;
171155
}
172156

173157
static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask,
@@ -216,12 +200,10 @@ static void __sbi_set_timer_v01(uint64_t stime_value)
216200
sbi_major_version(), sbi_minor_version());
217201
}
218202

219-
static int __sbi_send_ipi_v01(const struct cpumask *cpu_mask)
203+
static void __sbi_send_ipi_v01(unsigned int cpu)
220204
{
221205
pr_warn("IPI extension is not available in SBI v%lu.%lu\n",
222206
sbi_major_version(), sbi_minor_version());
223-
224-
return 0;
225207
}
226208

227209
static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask,
@@ -248,55 +230,18 @@ static void __sbi_set_timer_v02(uint64_t stime_value)
248230
#endif
249231
}
250232

251-
static int __sbi_send_ipi_v02(const struct cpumask *cpu_mask)
233+
static void __sbi_send_ipi_v02(unsigned int cpu)
252234
{
253-
unsigned long hartid, cpuid, hmask = 0, hbase = 0, htop = 0;
254-
struct sbiret ret = {0};
255235
int result;
236+
struct sbiret ret = {0};
256237

257-
if (!cpu_mask || cpumask_empty(cpu_mask))
258-
cpu_mask = cpu_online_mask;
259-
260-
for_each_cpu(cpuid, cpu_mask) {
261-
hartid = cpuid_to_hartid_map(cpuid);
262-
if (hmask) {
263-
if (hartid + BITS_PER_LONG <= htop ||
264-
hbase + BITS_PER_LONG <= hartid) {
265-
ret = sbi_ecall(SBI_EXT_IPI,
266-
SBI_EXT_IPI_SEND_IPI, hmask,
267-
hbase, 0, 0, 0, 0);
268-
if (ret.error)
269-
goto ecall_failed;
270-
hmask = 0;
271-
} else if (hartid < hbase) {
272-
/* shift the mask to fit lower hartid */
273-
hmask <<= hbase - hartid;
274-
hbase = hartid;
275-
}
276-
}
277-
if (!hmask) {
278-
hbase = hartid;
279-
htop = hartid;
280-
} else if (hartid > htop) {
281-
htop = hartid;
282-
}
283-
hmask |= BIT(hartid - hbase);
284-
}
285-
286-
if (hmask) {
287-
ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI,
288-
hmask, hbase, 0, 0, 0, 0);
289-
if (ret.error)
290-
goto ecall_failed;
238+
ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI,
239+
1UL, cpuid_to_hartid_map(cpu), 0, 0, 0, 0);
240+
if (ret.error) {
241+
result = sbi_err_map_linux_errno(ret.error);
242+
pr_err("%s: hbase = [%lu] failed (error [%d])\n",
243+
__func__, cpuid_to_hartid_map(cpu), result);
291244
}
292-
293-
return 0;
294-
295-
ecall_failed:
296-
result = sbi_err_map_linux_errno(ret.error);
297-
pr_err("%s: hbase = [%lu] hmask = [0x%lx] failed (error [%d])\n",
298-
__func__, hbase, hmask, result);
299-
return result;
300245
}
301246

302247
static int __sbi_rfence_v02_call(unsigned long fid, unsigned long hmask,
@@ -410,13 +355,11 @@ void sbi_set_timer(uint64_t stime_value)
410355

411356
/**
412357
* sbi_send_ipi() - Send an IPI to any hart.
413-
* @cpu_mask: A cpu mask containing all the target harts.
414-
*
415-
* Return: 0 on success, appropriate linux error code otherwise.
358+
* @cpu: Logical id of the target CPU.
416359
*/
417-
int sbi_send_ipi(const struct cpumask *cpu_mask)
360+
void sbi_send_ipi(unsigned int cpu)
418361
{
419-
return __sbi_send_ipi(cpu_mask);
362+
__sbi_send_ipi(cpu);
420363
}
421364
EXPORT_SYMBOL(sbi_send_ipi);
422365

@@ -641,21 +584,6 @@ long sbi_get_mimpid(void)
641584
}
642585
EXPORT_SYMBOL_GPL(sbi_get_mimpid);
643586

644-
static void sbi_send_cpumask_ipi(const struct cpumask *target)
645-
{
646-
sbi_send_ipi(target);
647-
}
648-
649-
static void sbi_ipi_clear(void)
650-
{
651-
csr_clear(CSR_IP, IE_SIE);
652-
}
653-
654-
static const struct riscv_ipi_ops sbi_ipi_ops = {
655-
.ipi_inject = sbi_send_cpumask_ipi,
656-
.ipi_clear = sbi_ipi_clear
657-
};
658-
659587
void __init sbi_init(void)
660588
{
661589
int ret;
@@ -702,6 +630,4 @@ void __init sbi_init(void)
702630
__sbi_send_ipi = __sbi_send_ipi_v01;
703631
__sbi_rfence = __sbi_rfence_v01;
704632
}
705-
706-
riscv_set_ipi_ops(&sbi_ipi_ops);
707633
}

0 commit comments

Comments
 (0)