Message ID | 20200519161755.209565-7-maz@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | arm/arm64: Turning IPIs into normal interrupts | expand |
Hi Marc, On Tue, 19 May 2020 at 21:48, Marc Zyngier <maz@kernel.org> wrote: > > Change the way we deal with GICv3 SGIs by turning them into proper > IRQs, and calling into the arch code to register the interrupt range > instead of a callback. > > Signed-off-by: Marc Zyngier <maz@kernel.org> > --- > drivers/irqchip/irq-gic-v3.c | 91 +++++++++++++++++++++--------------- > 1 file changed, 53 insertions(+), 38 deletions(-) > > diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c > index 23d7c87da407..d57289057b75 100644 > --- a/drivers/irqchip/irq-gic-v3.c > +++ b/drivers/irqchip/irq-gic-v3.c > @@ -36,6 +36,9 @@ > #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) > #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1) > > +#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) > +#define GIC_IRQ_TYPE_SGI (GIC_IRQ_TYPE_LPI + 2) > + > struct redist_region { > void __iomem *redist_base; > phys_addr_t phys_base; > @@ -657,38 +660,14 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs > if ((irqnr >= 1020 && irqnr <= 1023)) > return; > > - /* Treat anything but SGIs in a uniform way */ > - if (likely(irqnr > 15)) { > - int err; > - > - if (static_branch_likely(&supports_deactivate_key)) > - gic_write_eoir(irqnr); > - else > - isb(); > - > - err = handle_domain_irq(gic_data.domain, irqnr, regs); > - if (err) { > - WARN_ONCE(true, "Unexpected interrupt received!\n"); > - gic_deactivate_unhandled(irqnr); > - } > - return; > - } > - if (irqnr < 16) { > + if (static_branch_likely(&supports_deactivate_key)) > gic_write_eoir(irqnr); > - if (static_branch_likely(&supports_deactivate_key)) > - gic_write_dir(irqnr); > -#ifdef CONFIG_SMP > - /* > - * Unlike GICv2, we don't need an smp_rmb() here. > - * The control dependency from gic_read_iar to > - * the ISB in gic_write_eoir is enough to ensure > - * that any shared data read by handle_IPI will > - * be read after the ACK. > - */ > - handle_IPI(irqnr, regs); > -#else > - WARN_ONCE(true, "Unexpected SGI received!\n"); > -#endif > + else > + isb(); > + > + if (handle_domain_irq(gic_data.domain, irqnr, regs)) { > + WARN_ONCE(true, "Unexpected interrupt received!\n"); > + gic_deactivate_unhandled(irqnr); > } > } > > @@ -1136,11 +1115,11 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) > gic_write_sgi1r(val); > } > > -static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) > +static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask) > { > int cpu; > > - if (WARN_ON(irq >= 16)) > + if (WARN_ON(d->hwirq >= 16)) > return; > > /* > @@ -1154,7 +1133,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) > u16 tlist; > > tlist = gic_compute_target_list(&cpu, mask, cluster_id); > - gic_send_sgi(cluster_id, tlist, irq); > + gic_send_sgi(cluster_id, tlist, d->hwirq); > } > > /* Force the above writes to ICC_SGI1R_EL1 to be executed */ > @@ -1163,10 +1142,36 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) > > static void gic_smp_init(void) > { > - set_smp_cross_call(gic_raise_softirq); > + struct irq_fwspec sgi_fwspec = { > + .fwnode = gic_data.fwnode, > + }; > + int base_sgi; > + > cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, > "irqchip/arm/gicv3:starting", > gic_starting_cpu, NULL); > + > + if (is_of_node(gic_data.fwnode)) { > + /* DT */ > + sgi_fwspec.param_count = 3; > + sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI; > + sgi_fwspec.param[1] = 0; > + sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING; > + } else { > + /* ACPI */ > + sgi_fwspec.param_count = 2; > + sgi_fwspec.param[0] = 0; > + sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING; > + } > + > + /* Register all 8 non-secure SGIs */ > + base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8, > + NUMA_NO_NODE, &sgi_fwspec, > + false, NULL); > + if (WARN_ON(base_sgi <= 0)) > + return; > + > + set_smp_ipi_range(base_sgi, 8); > } > > static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, > @@ -1215,6 +1220,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, > } > #else > #define gic_set_affinity NULL > +#define gic_ipi_send_mask NULL > #define gic_smp_init() do { } while(0) > #endif > > @@ -1257,6 +1263,7 @@ static struct irq_chip gic_chip = { > .irq_set_irqchip_state = gic_irq_set_irqchip_state, > .irq_nmi_setup = gic_irq_nmi_setup, > .irq_nmi_teardown = gic_irq_nmi_teardown, > + .ipi_send_mask = gic_ipi_send_mask, > .flags = IRQCHIP_SET_TYPE_MASKED | > IRQCHIP_SKIP_SET_WAKE | > IRQCHIP_MASK_ON_SUSPEND, It looks like you missed to update "struct irq_chip gic_eoimode1_chip" with similar change as follows: diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 2a09634..ceef63b 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1291,6 +1291,7 @@ static struct irq_chip gic_eoimode1_chip = { .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, .irq_nmi_setup = gic_irq_nmi_setup, .irq_nmi_teardown = gic_irq_nmi_teardown, + .ipi_send_mask = gic_ipi_send_mask, .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, After incorporating this change, your patch-set works fine on my Developerbox machine. -Sumit > @@ -1289,6 +1296,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, > > switch (__get_intid_range(hw)) { > case SGI_RANGE: > + irq_set_percpu_devid(irq); > + irq_domain_set_info(d, irq, hw, chip, d->host_data, > + handle_percpu_devid_fasteoi_ipi, > + NULL, NULL); > + irq_set_status_flags(irq, IRQ_NOAUTOEN); > + break; > + > case PPI_RANGE: > case EPPI_RANGE: > irq_set_percpu_devid(irq); > @@ -1319,8 +1333,6 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, > return 0; > } > > -#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) > - > static int gic_irq_domain_translate(struct irq_domain *d, > struct irq_fwspec *fwspec, > unsigned long *hwirq, > @@ -1353,6 +1365,9 @@ static int gic_irq_domain_translate(struct irq_domain *d, > else > *hwirq += 16; > break; > + case GIC_IRQ_TYPE_SGI: > + *hwirq = fwspec->param[1]; > + break; > default: > return -EINVAL; > } > @@ -1657,9 +1672,9 @@ static int __init gic_init_bases(void __iomem *dist_base, > > gic_update_rdist_properties(); > > - gic_smp_init(); > gic_dist_init(); > gic_cpu_init(); > + gic_smp_init(); > gic_cpu_pm_init(); > > if (gic_dist_supports_lpis()) { > -- > 2.26.2 >
Hi Sumit, On 2020-05-20 10:52, Sumit Garg wrote: > Hi Marc, > > On Tue, 19 May 2020 at 21:48, Marc Zyngier <maz@kernel.org> wrote: >> >> Change the way we deal with GICv3 SGIs by turning them into proper >> IRQs, and calling into the arch code to register the interrupt range >> instead of a callback. >> >> Signed-off-by: Marc Zyngier <maz@kernel.org> >> --- >> drivers/irqchip/irq-gic-v3.c | 91 >> +++++++++++++++++++++--------------- >> 1 file changed, 53 insertions(+), 38 deletions(-) >> >> diff --git a/drivers/irqchip/irq-gic-v3.c >> b/drivers/irqchip/irq-gic-v3.c >> index 23d7c87da407..d57289057b75 100644 >> --- a/drivers/irqchip/irq-gic-v3.c >> +++ b/drivers/irqchip/irq-gic-v3.c >> @@ -36,6 +36,9 @@ >> #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) >> #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1) >> >> +#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) >> +#define GIC_IRQ_TYPE_SGI (GIC_IRQ_TYPE_LPI + 2) >> + >> struct redist_region { >> void __iomem *redist_base; >> phys_addr_t phys_base; >> @@ -657,38 +660,14 @@ static asmlinkage void __exception_irq_entry >> gic_handle_irq(struct pt_regs *regs >> if ((irqnr >= 1020 && irqnr <= 1023)) >> return; >> >> - /* Treat anything but SGIs in a uniform way */ >> - if (likely(irqnr > 15)) { >> - int err; >> - >> - if (static_branch_likely(&supports_deactivate_key)) >> - gic_write_eoir(irqnr); >> - else >> - isb(); >> - >> - err = handle_domain_irq(gic_data.domain, irqnr, regs); >> - if (err) { >> - WARN_ONCE(true, "Unexpected interrupt >> received!\n"); >> - gic_deactivate_unhandled(irqnr); >> - } >> - return; >> - } >> - if (irqnr < 16) { >> + if (static_branch_likely(&supports_deactivate_key)) >> gic_write_eoir(irqnr); >> - if (static_branch_likely(&supports_deactivate_key)) >> - gic_write_dir(irqnr); >> -#ifdef CONFIG_SMP >> - /* >> - * Unlike GICv2, we don't need an smp_rmb() here. >> - * The control dependency from gic_read_iar to >> - * the ISB in gic_write_eoir is enough to ensure >> - * that any shared data read by handle_IPI will >> - * be read after the ACK. >> - */ >> - handle_IPI(irqnr, regs); >> -#else >> - WARN_ONCE(true, "Unexpected SGI received!\n"); >> -#endif >> + else >> + isb(); >> + >> + if (handle_domain_irq(gic_data.domain, irqnr, regs)) { >> + WARN_ONCE(true, "Unexpected interrupt received!\n"); >> + gic_deactivate_unhandled(irqnr); >> } >> } >> >> @@ -1136,11 +1115,11 @@ static void gic_send_sgi(u64 cluster_id, u16 >> tlist, unsigned int irq) >> gic_write_sgi1r(val); >> } >> >> -static void gic_raise_softirq(const struct cpumask *mask, unsigned >> int irq) >> +static void gic_ipi_send_mask(struct irq_data *d, const struct >> cpumask *mask) >> { >> int cpu; >> >> - if (WARN_ON(irq >= 16)) >> + if (WARN_ON(d->hwirq >= 16)) >> return; >> >> /* >> @@ -1154,7 +1133,7 @@ static void gic_raise_softirq(const struct >> cpumask *mask, unsigned int irq) >> u16 tlist; >> >> tlist = gic_compute_target_list(&cpu, mask, >> cluster_id); >> - gic_send_sgi(cluster_id, tlist, irq); >> + gic_send_sgi(cluster_id, tlist, d->hwirq); >> } >> >> /* Force the above writes to ICC_SGI1R_EL1 to be executed */ >> @@ -1163,10 +1142,36 @@ static void gic_raise_softirq(const struct >> cpumask *mask, unsigned int irq) >> >> static void gic_smp_init(void) >> { >> - set_smp_cross_call(gic_raise_softirq); >> + struct irq_fwspec sgi_fwspec = { >> + .fwnode = gic_data.fwnode, >> + }; >> + int base_sgi; >> + >> cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, >> "irqchip/arm/gicv3:starting", >> gic_starting_cpu, NULL); >> + >> + if (is_of_node(gic_data.fwnode)) { >> + /* DT */ >> + sgi_fwspec.param_count = 3; >> + sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI; >> + sgi_fwspec.param[1] = 0; >> + sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING; >> + } else { >> + /* ACPI */ >> + sgi_fwspec.param_count = 2; >> + sgi_fwspec.param[0] = 0; >> + sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING; >> + } >> + >> + /* Register all 8 non-secure SGIs */ >> + base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8, >> + NUMA_NO_NODE, &sgi_fwspec, >> + false, NULL); >> + if (WARN_ON(base_sgi <= 0)) >> + return; >> + >> + set_smp_ipi_range(base_sgi, 8); >> } >> >> static int gic_set_affinity(struct irq_data *d, const struct cpumask >> *mask_val, >> @@ -1215,6 +1220,7 @@ static int gic_set_affinity(struct irq_data *d, >> const struct cpumask *mask_val, >> } >> #else >> #define gic_set_affinity NULL >> +#define gic_ipi_send_mask NULL >> #define gic_smp_init() do { } while(0) >> #endif >> >> @@ -1257,6 +1263,7 @@ static struct irq_chip gic_chip = { >> .irq_set_irqchip_state = gic_irq_set_irqchip_state, >> .irq_nmi_setup = gic_irq_nmi_setup, >> .irq_nmi_teardown = gic_irq_nmi_teardown, >> + .ipi_send_mask = gic_ipi_send_mask, >> .flags = IRQCHIP_SET_TYPE_MASKED | >> IRQCHIP_SKIP_SET_WAKE | >> IRQCHIP_MASK_ON_SUSPEND, > > It looks like you missed to update "struct irq_chip gic_eoimode1_chip" > with similar change as follows: > > diff --git a/drivers/irqchip/irq-gic-v3.c > b/drivers/irqchip/irq-gic-v3.c > index 2a09634..ceef63b 100644 > --- a/drivers/irqchip/irq-gic-v3.c > +++ b/drivers/irqchip/irq-gic-v3.c > @@ -1291,6 +1291,7 @@ static struct irq_chip gic_eoimode1_chip = { > .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, > .irq_nmi_setup = gic_irq_nmi_setup, > .irq_nmi_teardown = gic_irq_nmi_teardown, > + .ipi_send_mask = gic_ipi_send_mask, > .flags = IRQCHIP_SET_TYPE_MASKED | > IRQCHIP_SKIP_SET_WAKE | > IRQCHIP_MASK_ON_SUSPEND, > > After incorporating this change, your patch-set works fine on my > Developerbox machine. Huh, well spotted. As I said, it has only been tested as guests, hence not hitting this path. Time to throw it at the bigger stuff. Thanks, M.
On 19/05/20 17:17, Marc Zyngier wrote: > Change the way we deal with GICv3 SGIs by turning them into proper > IRQs, and calling into the arch code to register the interrupt range > instead of a callback. > > Signed-off-by: Marc Zyngier <maz@kernel.org> > --- > drivers/irqchip/irq-gic-v3.c | 91 +++++++++++++++++++++--------------- > 1 file changed, 53 insertions(+), 38 deletions(-) > > diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c > index 23d7c87da407..d57289057b75 100644 > --- a/drivers/irqchip/irq-gic-v3.c > +++ b/drivers/irqchip/irq-gic-v3.c > @@ -1163,10 +1142,36 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) > > static void gic_smp_init(void) > { > - set_smp_cross_call(gic_raise_softirq); > + struct irq_fwspec sgi_fwspec = { > + .fwnode = gic_data.fwnode, > + }; > + int base_sgi; > + > cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, > "irqchip/arm/gicv3:starting", > gic_starting_cpu, NULL); > + > + if (is_of_node(gic_data.fwnode)) { > + /* DT */ > + sgi_fwspec.param_count = 3; > + sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI; > + sgi_fwspec.param[1] = 0; > + sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING; > + } else { > + /* ACPI */ > + sgi_fwspec.param_count = 2; > + sgi_fwspec.param[0] = 0; > + sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING; > + } > + > + /* Register all 8 non-secure SGIs */ > + base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8, > + NUMA_NO_NODE, &sgi_fwspec, > + false, NULL); So IIUC using irq_reserve_ipi() would require us to have a separate IPI domain, so instead here we can use a fwspec + the 'regular' GIC domain. One thing I see is that by not going through irq_reserve_ipi(), we don't set data->common->ipi_offset. I think this is all kzalloc'd, and we want an offset of 0 so it all works out, but this feels somewhat fragile. > + if (WARN_ON(base_sgi <= 0)) > + return; > + > + set_smp_ipi_range(base_sgi, 8); > } > > static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, > @@ -1289,6 +1296,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, > > switch (__get_intid_range(hw)) { > case SGI_RANGE: > + irq_set_percpu_devid(irq); > + irq_domain_set_info(d, irq, hw, chip, d->host_data, > + handle_percpu_devid_fasteoi_ipi, > + NULL, NULL); > + irq_set_status_flags(irq, IRQ_NOAUTOEN); FWIW IRQ_NOAUTOEN is already set by irq_set_percpu_devid_flags(), so that's not required. I know we do that for (E)PPIs, I think I already have a small patch stashed somewhere regarding that. > + break; > + > case PPI_RANGE: > case EPPI_RANGE: > irq_set_percpu_devid(irq);
Hi Valentin, On Thu, 21 May 2020 15:04:54 +0100 Valentin Schneider <valentin.schneider@arm.com> wrote: > On 19/05/20 17:17, Marc Zyngier wrote: > > Change the way we deal with GICv3 SGIs by turning them into proper > > IRQs, and calling into the arch code to register the interrupt range > > instead of a callback. > > > > Signed-off-by: Marc Zyngier <maz@kernel.org> > > --- > > drivers/irqchip/irq-gic-v3.c | 91 +++++++++++++++++++++--------------- > > 1 file changed, 53 insertions(+), 38 deletions(-) > > > > diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c > > index 23d7c87da407..d57289057b75 100644 > > --- a/drivers/irqchip/irq-gic-v3.c > > +++ b/drivers/irqchip/irq-gic-v3.c > > @@ -1163,10 +1142,36 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) > > > > static void gic_smp_init(void) > > { > > - set_smp_cross_call(gic_raise_softirq); > > + struct irq_fwspec sgi_fwspec = { > > + .fwnode = gic_data.fwnode, > > + }; > > + int base_sgi; > > + > > cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, > > "irqchip/arm/gicv3:starting", > > gic_starting_cpu, NULL); > > + > > + if (is_of_node(gic_data.fwnode)) { > > + /* DT */ > > + sgi_fwspec.param_count = 3; > > + sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI; > > + sgi_fwspec.param[1] = 0; > > + sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING; > > + } else { > > + /* ACPI */ > > + sgi_fwspec.param_count = 2; > > + sgi_fwspec.param[0] = 0; > > + sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING; > > + } > > + > > + /* Register all 8 non-secure SGIs */ > > + base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8, > > + NUMA_NO_NODE, &sgi_fwspec, > > + false, NULL); > > So IIUC using irq_reserve_ipi() would require us to have a separate IPI > domain, so instead here we can use a fwspec + the 'regular' GIC domain. Indeed. Using an IPI domain wouldn't bring much. But the major point against the current state of the IPI domain is that it sucks a bit for our use case. We want interrupts to be contiguous in the Linux IRQ space, and the IPI allocator prevents this. But maybe I should just bite the bullet and hack that as well. > One thing I see is that by not going through irq_reserve_ipi(), we don't set > data->common->ipi_offset. I think this is all kzalloc'd, and we want an > offset of 0 so it all works out, but this feels somewhat fragile. So far, nothing is using this field on the limited piece of code we use. But I agree, not the nicest behaviour. > > + if (WARN_ON(base_sgi <= 0)) > > + return; > > + > > + set_smp_ipi_range(base_sgi, 8); > > } > > > > static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, > > @@ -1289,6 +1296,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, > > > > switch (__get_intid_range(hw)) { > > case SGI_RANGE: > > + irq_set_percpu_devid(irq); > > + irq_domain_set_info(d, irq, hw, chip, d->host_data, > > + handle_percpu_devid_fasteoi_ipi, > > + NULL, NULL); > > + irq_set_status_flags(irq, IRQ_NOAUTOEN); > > FWIW IRQ_NOAUTOEN is already set by irq_set_percpu_devid_flags(), so that's > not required. I know we do that for (E)PPIs, I think I already have a small > patch stashed somewhere regarding that. Already merged! ;-) Thanks, M.
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 23d7c87da407..d57289057b75 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -36,6 +36,9 @@ #define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0) #define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1) +#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) +#define GIC_IRQ_TYPE_SGI (GIC_IRQ_TYPE_LPI + 2) + struct redist_region { void __iomem *redist_base; phys_addr_t phys_base; @@ -657,38 +660,14 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs if ((irqnr >= 1020 && irqnr <= 1023)) return; - /* Treat anything but SGIs in a uniform way */ - if (likely(irqnr > 15)) { - int err; - - if (static_branch_likely(&supports_deactivate_key)) - gic_write_eoir(irqnr); - else - isb(); - - err = handle_domain_irq(gic_data.domain, irqnr, regs); - if (err) { - WARN_ONCE(true, "Unexpected interrupt received!\n"); - gic_deactivate_unhandled(irqnr); - } - return; - } - if (irqnr < 16) { + if (static_branch_likely(&supports_deactivate_key)) gic_write_eoir(irqnr); - if (static_branch_likely(&supports_deactivate_key)) - gic_write_dir(irqnr); -#ifdef CONFIG_SMP - /* - * Unlike GICv2, we don't need an smp_rmb() here. - * The control dependency from gic_read_iar to - * the ISB in gic_write_eoir is enough to ensure - * that any shared data read by handle_IPI will - * be read after the ACK. - */ - handle_IPI(irqnr, regs); -#else - WARN_ONCE(true, "Unexpected SGI received!\n"); -#endif + else + isb(); + + if (handle_domain_irq(gic_data.domain, irqnr, regs)) { + WARN_ONCE(true, "Unexpected interrupt received!\n"); + gic_deactivate_unhandled(irqnr); } } @@ -1136,11 +1115,11 @@ static void gic_send_sgi(u64 cluster_id, u16 tlist, unsigned int irq) gic_write_sgi1r(val); } -static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) +static void gic_ipi_send_mask(struct irq_data *d, const struct cpumask *mask) { int cpu; - if (WARN_ON(irq >= 16)) + if (WARN_ON(d->hwirq >= 16)) return; /* @@ -1154,7 +1133,7 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) u16 tlist; tlist = gic_compute_target_list(&cpu, mask, cluster_id); - gic_send_sgi(cluster_id, tlist, irq); + gic_send_sgi(cluster_id, tlist, d->hwirq); } /* Force the above writes to ICC_SGI1R_EL1 to be executed */ @@ -1163,10 +1142,36 @@ static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) static void gic_smp_init(void) { - set_smp_cross_call(gic_raise_softirq); + struct irq_fwspec sgi_fwspec = { + .fwnode = gic_data.fwnode, + }; + int base_sgi; + cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING, "irqchip/arm/gicv3:starting", gic_starting_cpu, NULL); + + if (is_of_node(gic_data.fwnode)) { + /* DT */ + sgi_fwspec.param_count = 3; + sgi_fwspec.param[0] = GIC_IRQ_TYPE_SGI; + sgi_fwspec.param[1] = 0; + sgi_fwspec.param[2] = IRQ_TYPE_EDGE_RISING; + } else { + /* ACPI */ + sgi_fwspec.param_count = 2; + sgi_fwspec.param[0] = 0; + sgi_fwspec.param[1] = IRQ_TYPE_EDGE_RISING; + } + + /* Register all 8 non-secure SGIs */ + base_sgi = __irq_domain_alloc_irqs(gic_data.domain, -1, 8, + NUMA_NO_NODE, &sgi_fwspec, + false, NULL); + if (WARN_ON(base_sgi <= 0)) + return; + + set_smp_ipi_range(base_sgi, 8); } static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, @@ -1215,6 +1220,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, } #else #define gic_set_affinity NULL +#define gic_ipi_send_mask NULL #define gic_smp_init() do { } while(0) #endif @@ -1257,6 +1263,7 @@ static struct irq_chip gic_chip = { .irq_set_irqchip_state = gic_irq_set_irqchip_state, .irq_nmi_setup = gic_irq_nmi_setup, .irq_nmi_teardown = gic_irq_nmi_teardown, + .ipi_send_mask = gic_ipi_send_mask, .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, @@ -1289,6 +1296,13 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, switch (__get_intid_range(hw)) { case SGI_RANGE: + irq_set_percpu_devid(irq); + irq_domain_set_info(d, irq, hw, chip, d->host_data, + handle_percpu_devid_fasteoi_ipi, + NULL, NULL); + irq_set_status_flags(irq, IRQ_NOAUTOEN); + break; + case PPI_RANGE: case EPPI_RANGE: irq_set_percpu_devid(irq); @@ -1319,8 +1333,6 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, return 0; } -#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1) - static int gic_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, @@ -1353,6 +1365,9 @@ static int gic_irq_domain_translate(struct irq_domain *d, else *hwirq += 16; break; + case GIC_IRQ_TYPE_SGI: + *hwirq = fwspec->param[1]; + break; default: return -EINVAL; } @@ -1657,9 +1672,9 @@ static int __init gic_init_bases(void __iomem *dist_base, gic_update_rdist_properties(); - gic_smp_init(); gic_dist_init(); gic_cpu_init(); + gic_smp_init(); gic_cpu_pm_init(); if (gic_dist_supports_lpis()) {
Change the way we deal with GICv3 SGIs by turning them into proper IRQs, and calling into the arch code to register the interrupt range instead of a callback. Signed-off-by: Marc Zyngier <maz@kernel.org> --- drivers/irqchip/irq-gic-v3.c | 91 +++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 38 deletions(-)