Skip to content
Permalink
Browse files
arm64: vipi: vIPI code adjustments
Some systems, such as the Apple M1 SoC, do not have more than one or
two hardware IPIs that can be distinguished and masked
independently. For such systems, it is necessary to provide "virtual"
IPIs, but since there are at least two IRQ controllers that need this
feature, let's move it into common code.

The new entrypoint accepts a potentially non-maskable single per-CPU
IPI interrupt and provides up to 32 "virtual" per-CPU IPIs that are
maskable, can be distinguished, and live in an IPI domain.

set_smp_ipi_range is changed to transparently enable the vIPI layer on
IPI-challenged systems.

TODO: move vipi_init definition to header file
TODO: return a value from set_smp_ipi_range so drivers know whether to
set irq domain flags.
TODO: make conditional on IPI-challenged archs.

Signed-off-by: Pip Cet <pipcet@gmail.com>
  • Loading branch information
pipcet committed Aug 11, 2021
1 parent 9dfd864 commit 28a9ae246e6f38ab4b3264e7cf8a11d9f47535ad
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 10 deletions.
@@ -314,6 +314,9 @@ config ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE
config SMP
def_bool y

config IPI_FUNNELING
def_bool y

config KERNEL_MODE_NEON
def_bool y

@@ -73,6 +73,7 @@ obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o
obj-$(CONFIG_ARM64_MTE) += mte.o
obj-y += vdso-wrap.o
obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o
obj-$(CONFIG_IPI_FUNNELING) += vipi.o

obj-y += probes/
head-y := head.o
@@ -979,10 +979,26 @@ static void ipi_teardown(int cpu)
}
#endif

#ifdef CONFIG_IPI_FUNNELING
extern int __init vipi_init(struct irq_data *hwirq);
#else
static inline int vipi_init(irqdata)
{
return -EINVAL;
}
#endif

void __init set_smp_ipi_range(int ipi_base, int n)
{
int i;

if (n < NR_IPI) {
int ret;
BUG_ON(n < 1);
ret = vipi_init(irq_get_irq_data(ipi_base));
if (ret >= 0)
return;
}
WARN_ON(n < NR_IPI);
nr_ipi = min(n, NR_IPI);

@@ -21,6 +21,19 @@
#include <asm/sysreg.h>
#include <asm/virt.h>

struct vipi_irq_chip {
struct irq_domain *domain;
struct irq_data *hwirq;
};

#define NR_SWIPI 32

static DEFINE_PER_CPU(atomic_t, vipi_flag);
static DEFINE_PER_CPU(atomic_t, vipi_enable);

static struct vipi_irq_chip *vipi_irqc;

static void handle_ipi(struct irq_desc *d);
/*
* IPI irqchip
*/
@@ -83,8 +96,10 @@ static void vipi_send_mask(struct irq_data *d, const struct cpumask *mask)
smp_mb__after_atomic();

if (!(pending & irq_bit) &&
(atomic_read(per_cpu_ptr(&aic_vipi_enable, cpu)) & irq_bit))
send |= AIC_IPI_SEND_CPU(cpu);
(atomic_read(per_cpu_ptr(&vipi_enable, cpu)) & irq_bit)) {
cpumask_set_cpu(cpu, &sendmask);
send = true;
}
}

/*
@@ -94,7 +109,7 @@ static void vipi_send_mask(struct irq_data *d, const struct cpumask *mask)
* already ordered after the vIPI flag write.
*/
if (send)
aic_ic_write(ic, AIC_IPI_SEND, send);
ipi_send_mask(ic->hwirq->irq, &sendmask);
}

static struct irq_chip vipi_chip = {
@@ -132,9 +147,12 @@ static void handle_ipi(struct irq_desc *d)
*/
firing = atomic_fetch_andnot(enabled, this_cpu_ptr(&vipi_flag)) & enabled;

for_each_set_bit(i, &firing, AIC_NR_SWIPI)
handle_domain_irq(aic_irqc->ipi_domain, i, regs);
for_each_set_bit(i, &firing, NR_SWIPI) {
struct irq_desc *nd =
irq_resolve_mapping(vipi_irqc->domain, i);

handle_irq_desc(nd);
}
}

static int vipi_alloc(struct irq_domain *d, unsigned int virq,
@@ -154,6 +172,7 @@ static int vipi_alloc(struct irq_domain *d, unsigned int virq,
static void vipi_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs)
{
/* Not freeing IPIs */
WARN_ON(1);
}

static const struct irq_domain_ops vipi_domain_ops = {
@@ -165,8 +184,12 @@ static int vipi_init_smp(struct vipi_irq_chip *irqc)
{
struct irq_domain *vipi_domain;
int base_ipi;
struct fwnode_handle *fwnode;

fwnode = __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0,
"vIPI", NULL);

ipi_domain = irq_domain_create_linear(irqc->hw_domain->fwnode, AIC_NR_SWIPI,
vipi_domain = irq_domain_create_linear(fwnode, NR_SWIPI,
&vipi_domain_ops, irqc);
if (WARN_ON(!vipi_domain))
return -ENOMEM;
@@ -179,7 +202,7 @@ static int vipi_init_smp(struct vipi_irq_chip *irqc)

if (WARN_ON(base_ipi < 0)) {
irq_domain_remove(vipi_domain);
return -ENODEV;
return -ENOMEM;
}

set_smp_ipi_range(base_ipi, NR_SWIPI);
@@ -189,19 +212,24 @@ static int vipi_init_smp(struct vipi_irq_chip *irqc)
return 0;
}

static int aic_init_cpu(unsigned int cpu)
int __init vipi_init(struct irq_data *hwirq)
{
struct vipi_irq_chip *irqc;

irqc = kzalloc(sizeof(*irqc), GFP_KERNEL);
if (!irqc)
return -ENOMEM;

irqc->hwirq = hwirq;

if (vipi_init_smp(irqc))
return -ENOMEM;

vipi_irqc = irqc;

irq_set_handler_locked(hwirq, handle_ipi);

pr_info("Initialized with %d IRQs, %d FIQs, %d vIPIs\n",
irqc->nr_hw, AIC_NR_FIQ, AIC_NR_SWIPI);
pr_info("Initialized with %d vIPIs\n", NR_SWIPI);

return 0;
}

0 comments on commit 28a9ae2

Please sign in to comment.