Skip to content

Commit

Permalink
RISC-V: Treat IPIs as normal Linux IRQs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
avpatel committed Apr 15, 2022
1 parent 9670949 commit 4036f0a
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 155 deletions.
2 changes: 2 additions & 0 deletions arch/riscv/Kconfig
Expand Up @@ -57,6 +57,8 @@ config RISCV
select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO
select GENERIC_IDLE_POLL_SETUP
select GENERIC_IOREMAP if MMU
select GENERIC_IRQ_IPI
select GENERIC_IRQ_IPI_MUX
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_IRQ_SHOW
select GENERIC_IRQ_SHOW_LEVEL
Expand Down
2 changes: 2 additions & 0 deletions arch/riscv/include/asm/sbi.h
Expand Up @@ -228,6 +228,7 @@ struct sbiret {
};

void sbi_init(void);
void sbi_ipi_init(void);
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
Expand Down Expand Up @@ -297,6 +298,7 @@ static inline unsigned long sbi_mk_version(unsigned long major,
int sbi_err_map_linux_errno(int err);
#else /* CONFIG_RISCV_SBI */
static inline int sbi_remote_fence_i(const struct cpumask *cpu_mask) { return -1; }
static inline void sbi_ipi_init(void) { }
static inline void sbi_init(void) {}
#endif /* CONFIG_RISCV_SBI */
#endif /* _ASM_RISCV_SBI_H */
35 changes: 21 additions & 14 deletions arch/riscv/include/asm/smp.h
Expand Up @@ -15,11 +15,6 @@
struct seq_file;
extern unsigned long boot_cpu_hartid;

struct riscv_ipi_ops {
void (*ipi_inject)(const struct cpumask *target);
void (*ipi_clear)(void);
};

#ifdef CONFIG_SMP
/*
* Mapping between linux logical cpu index and hartid.
Expand All @@ -33,9 +28,6 @@ void show_ipi_stats(struct seq_file *p, int prec);
/* SMP initialization hook for setup_arch */
void __init setup_smp(void);

/* Called from C code, this handles an IPI. */
void handle_IPI(struct pt_regs *regs);

/* Hook for the generic smp_call_function_many() routine. */
void arch_send_call_function_ipi_mask(struct cpumask *mask);

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

int riscv_hartid_to_cpuid(int hartid);

/* Set custom IPI operations */
void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops);
/* Enable IPI for CPU hotplug */
void riscv_ipi_enable(void);

/* Disable IPI for CPU hotplug */
void riscv_ipi_disable(void);

/* Clear IPI for current CPU */
void riscv_clear_ipi(void);
/* Check if IPI interrupt numbers are available */
bool riscv_ipi_have_virq_range(void);

/* Set the IPI interrupt numbers for arch (called by irqchip drivers) */
void riscv_ipi_set_virq_range(int virq, int nr_irqs);

/* Secondary hart entry */
asmlinkage void smp_callin(void);
Expand Down Expand Up @@ -82,11 +80,20 @@ static inline unsigned long cpuid_to_hartid_map(int cpu)
return boot_cpu_hartid;
}

static inline void riscv_set_ipi_ops(const struct riscv_ipi_ops *ops)
static inline void riscv_ipi_enable(void)
{
}

static inline void riscv_clear_ipi(void)
static inline void riscv_ipi_disable(void)
{
}

static inline bool riscv_ipi_have_virq_range(void)
{
return false;
}

static inline void riscv_ipi_set_virq_range(int virq, int nr)
{
}

Expand Down
1 change: 1 addition & 0 deletions arch/riscv/kernel/Makefile
Expand Up @@ -58,6 +58,7 @@ obj-$(CONFIG_TRACE_IRQFLAGS) += trace_irq.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
obj-$(CONFIG_RISCV_SBI) += sbi.o
obj-$(CONFIG_RISCV_SBI) += sbi-ipi.o
ifeq ($(CONFIG_RISCV_SBI), y)
obj-$(CONFIG_SMP) += cpu_ops_sbi.o
endif
Expand Down
3 changes: 2 additions & 1 deletion arch/riscv/kernel/cpu-hotplug.c
Expand Up @@ -13,7 +13,7 @@
#include <asm/irq.h>
#include <asm/cpu_ops.h>
#include <asm/numa.h>
#include <asm/sbi.h>
#include <asm/smp.h>

bool cpu_has_hotplug(unsigned int cpu)
{
Expand Down Expand Up @@ -43,6 +43,7 @@ int __cpu_disable(void)
remove_cpu_topology(cpu);
numa_remove_cpu(cpu);
set_cpu_online(cpu, false);
riscv_ipi_disable();
irq_migrate_all_off_this_cpu();

return ret;
Expand Down
3 changes: 2 additions & 1 deletion arch/riscv/kernel/irq.c
Expand Up @@ -10,7 +10,7 @@
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <asm/smp.h>
#include <asm/sbi.h>

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

Expand Down Expand Up @@ -39,4 +39,5 @@ void __init init_IRQ(void)
irqchip_init();
if (!handle_arch_irq)
panic("No interrupt controller found.");
sbi_ipi_init();
}
60 changes: 60 additions & 0 deletions arch/riscv/kernel/sbi-ipi.c
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Multiplex several IPIs over a single HW IPI.
*
* Copyright (c) 2022 Ventana Micro Systems Inc.
*/

#define pr_fmt(fmt) "riscv: " fmt
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <asm/sbi.h>

static void sbi_send_cpumask_ipi(unsigned int parent_virq,
const struct cpumask *target)
{
sbi_send_ipi(target);
}

static void sbi_ipi_clear(unsigned int parent_virq)
{
csr_clear(CSR_IP, IE_SIE);
}

static struct ipi_mux_ops sbi_ipi_ops = {
.ipi_mux_clear = sbi_ipi_clear,
.ipi_mux_send = sbi_send_cpumask_ipi,
};

void __init sbi_ipi_init(void)
{
int virq, parent_virq;
struct irq_domain *domain;

if (riscv_ipi_have_virq_range())
return;

domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
DOMAIN_BUS_ANY);
if (!domain) {
pr_err("unable to find INTC IRQ domain\n");
return;
}

parent_virq = irq_create_mapping(domain, RV_IRQ_SOFT);
if (!parent_virq) {
pr_err("unable to create INTC IRQ mapping\n");
return;
}

virq = ipi_mux_create(parent_virq, &sbi_ipi_ops);
if (virq <= 0) {
pr_err("unable to create muxed IPIs\n");
irq_dispose_mapping(parent_virq);
return;
}

riscv_ipi_set_virq_range(virq, IPI_MUX_NR_IRQS);
pr_info("providing IPIs using SBI IPI extension\n");
}
17 changes: 0 additions & 17 deletions arch/riscv/kernel/sbi.c
Expand Up @@ -638,21 +638,6 @@ long sbi_get_mimpid(void)
return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID);
}

static void sbi_send_cpumask_ipi(const struct cpumask *target)
{
sbi_send_ipi(target);
}

static void sbi_ipi_clear(void)
{
csr_clear(CSR_IP, IE_SIE);
}

static const struct riscv_ipi_ops sbi_ipi_ops = {
.ipi_inject = sbi_send_cpumask_ipi,
.ipi_clear = sbi_ipi_clear
};

void __init sbi_init(void)
{
int ret;
Expand Down Expand Up @@ -699,6 +684,4 @@ void __init sbi_init(void)
__sbi_send_ipi = __sbi_send_ipi_v01;
__sbi_rfence = __sbi_rfence_v01;
}

riscv_set_ipi_ops(&sbi_ipi_ops);
}

0 comments on commit 4036f0a

Please sign in to comment.