diff mbox series

[06/11] irqchip/gic-v3: Configure SGIs as standard interrupts

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

Commit Message

Marc Zyngier May 19, 2020, 4:17 p.m. UTC
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(-)

Comments

Sumit Garg May 20, 2020, 9:52 a.m. UTC | #1
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
>
Marc Zyngier May 20, 2020, 10:24 a.m. UTC | #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.
Valentin Schneider May 21, 2020, 2:04 p.m. UTC | #3
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);
Marc Zyngier June 12, 2020, 10:39 a.m. UTC | #4
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 mbox series

Patch

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()) {