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 Nov 7, 2022
1 parent 5629ad5 commit 64c5481
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 155 deletions.
2 changes: 2 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ config RISCV
select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO
select GENERIC_IDLE_POLL_SETUP
select GENERIC_IOREMAP if MMU
select GENERIC_IRQ_IPI if SMP
select GENERIC_IRQ_IPI_MUX if SMP
select GENERIC_IRQ_MULTI_HANDLER
select GENERIC_IRQ_SHOW
select GENERIC_IRQ_SHOW_LEVEL
Expand Down
7 changes: 7 additions & 0 deletions arch/riscv/include/asm/sbi.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,4 +327,11 @@ int sbi_err_map_linux_errno(int err);
static inline int sbi_remote_fence_i(const struct cpumask *cpu_mask) { return -1; }
static inline void sbi_init(void) {}
#endif /* CONFIG_RISCV_SBI */

#if IS_ENABLED(CONFIG_SMP) && IS_ENABLED(CONFIG_RISCV_SBI)
void sbi_ipi_init(void);
#else
static inline void sbi_ipi_init(void) { }
#endif

#endif /* _ASM_RISCV_SBI_H */
35 changes: 21 additions & 14 deletions arch/riscv/include/asm/smp.h
Original file line number Diff line number Diff line change
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(unsigned long 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);

/* 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
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
obj-$(CONFIG_RISCV_SBI) += sbi.o
ifeq ($(CONFIG_RISCV_SBI), y)
obj-$(CONFIG_SMP) += sbi-ipi.o
obj-$(CONFIG_SMP) += cpu_ops_sbi.o
endif
obj-$(CONFIG_HOTPLUG_CPU) += cpu-hotplug.o
Expand Down
3 changes: 2 additions & 1 deletion arch/riscv/kernel/cpu-hotplug.c
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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();
}
80 changes: 80 additions & 0 deletions arch/riscv/kernel/sbi-ipi.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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/cpu.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <asm/sbi.h>

static int sbi_ipi_virq;

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

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

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

static int sbi_ipi_dying_cpu(unsigned int cpu)
{
disable_percpu_irq(sbi_ipi_virq);
return 0;
}

static int sbi_ipi_starting_cpu(unsigned int cpu)
{
enable_percpu_irq(sbi_ipi_virq, irq_get_trigger_type(sbi_ipi_virq));
return 0;
}

void __init sbi_ipi_init(void)
{
int 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;
}

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

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

cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"irqchip/sbi-ipi:starting",
sbi_ipi_starting_cpu, sbi_ipi_dying_cpu);

riscv_ipi_set_virq_range(virq, BITS_PER_BYTE);
pr_info("providing IPIs using SBI IPI extension\n");
}
17 changes: 0 additions & 17 deletions arch/riscv/kernel/sbi.c
Original file line number Diff line number Diff line change
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 64c5481

Please sign in to comment.