lkml.org 
[lkml]   [2022]   [Jun]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.17 394/772] irqchip/gic-v3: Refactor ISB + EOIR at ack time
    Date
    From: Mark Rutland <mark.rutland@arm.com>

    [ Upstream commit 6efb50923771f392122f5ce69dfc43b08f16e449 ]

    There are cases where a context synchronization event is necessary
    between an IRQ being raised and being handled, and there are races such
    that we cannot rely upon the exception entry being subsequent to the
    interrupt being raised. To fix this, we place an ISB between a read of
    IAR and the subsequent invocation of an IRQ handler.

    When EOI mode 1 is in use, we need to EOI an interrupt prior to invoking
    its handler, and we have a write to EOIR for this. As this write to EOIR
    requires an ISB, and this is provided by the gic_write_eoir() helper, we
    omit the usual ISB in this case, with the logic being:

    | if (static_branch_likely(&supports_deactivate_key))
    | gic_write_eoir(irqnr);
    | else
    | isb();

    This is somewhat opaque, and it would be a little clearer if there were
    an unconditional ISB, with only the write to EOIR being conditional,
    e.g.

    | if (static_branch_likely(&supports_deactivate_key))
    | write_gicreg(irqnr, ICC_EOIR1_EL1);
    |
    | isb();

    This patch rewrites the code that way, with this logic factored into a
    new helper function with comments explaining what the ISB is for, as
    were originally laid out in commit:

    39a06b67c2c1256b ("irqchip/gic: Ensure we have an ISB between ack and ->handle_irq")

    Note that since then, we removed the IAR polling in commit:

    342677d70ab92142 ("irqchip/gic-v3: Remove acknowledge loop")

    ... which removed one of the two race conditions.

    For consistency, other portions of the driver are made to manipulate
    EOIR using write_gicreg() and explcit ISBs, and the gic_write_eoir()
    helper function is removed.

    There should be no functional change as a result of this patch.

    Signed-off-by: Mark Rutland <mark.rutland@arm.com>
    Cc: Marc Zyngier <maz@kernel.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Will Deacon <will.deacon@arm.com>
    Signed-off-by: Marc Zyngier <maz@kernel.org>
    Link: https://lore.kernel.org/r/20220513133038.226182-3-mark.rutland@arm.com
    Signed-off-by: Sasha Levin <sashal@kernel.org>
    ---
    arch/arm/include/asm/arch_gicv3.h | 7 +----
    arch/arm64/include/asm/arch_gicv3.h | 6 ----
    drivers/irqchip/irq-gic-v3.c | 43 ++++++++++++++++++++++-------
    3 files changed, 34 insertions(+), 22 deletions(-)

    diff --git a/arch/arm/include/asm/arch_gicv3.h b/arch/arm/include/asm/arch_gicv3.h
    index 413abfb42989..f82a819eb0db 100644
    --- a/arch/arm/include/asm/arch_gicv3.h
    +++ b/arch/arm/include/asm/arch_gicv3.h
    @@ -48,6 +48,7 @@ static inline u32 read_ ## a64(void) \
    return read_sysreg(a32); \
    } \

    +CPUIF_MAP(ICC_EOIR1, ICC_EOIR1_EL1)
    CPUIF_MAP(ICC_PMR, ICC_PMR_EL1)
    CPUIF_MAP(ICC_AP0R0, ICC_AP0R0_EL1)
    CPUIF_MAP(ICC_AP0R1, ICC_AP0R1_EL1)
    @@ -63,12 +64,6 @@ CPUIF_MAP(ICC_AP1R3, ICC_AP1R3_EL1)

    /* Low-level accessors */

    -static inline void gic_write_eoir(u32 irq)
    -{
    - write_sysreg(irq, ICC_EOIR1);
    - isb();
    -}
    -
    static inline void gic_write_dir(u32 val)
    {
    write_sysreg(val, ICC_DIR);
    diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
    index 4ad22c3135db..5a0f792492af 100644
    --- a/arch/arm64/include/asm/arch_gicv3.h
    +++ b/arch/arm64/include/asm/arch_gicv3.h
    @@ -26,12 +26,6 @@
    * sets the GP register's most significant bits to 0 with an explicit cast.
    */

    -static inline void gic_write_eoir(u32 irq)
    -{
    - write_sysreg_s(irq, SYS_ICC_EOIR1_EL1);
    - isb();
    -}
    -
    static __always_inline void gic_write_dir(u32 irq)
    {
    write_sysreg_s(irq, SYS_ICC_DIR_EL1);
    diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
    index 3e3afb30de50..c26f571fba0c 100644
    --- a/drivers/irqchip/irq-gic-v3.c
    +++ b/drivers/irqchip/irq-gic-v3.c
    @@ -556,7 +556,8 @@ static void gic_irq_nmi_teardown(struct irq_data *d)

    static void gic_eoi_irq(struct irq_data *d)
    {
    - gic_write_eoir(gic_irq(d));
    + write_gicreg(gic_irq(d), ICC_EOIR1_EL1);
    + isb();
    }

    static void gic_eoimode1_eoi_irq(struct irq_data *d)
    @@ -640,10 +641,38 @@ static void gic_deactivate_unhandled(u32 irqnr)
    if (irqnr < 8192)
    gic_write_dir(irqnr);
    } else {
    - gic_write_eoir(irqnr);
    + write_gicreg(irqnr, ICC_EOIR1_EL1);
    + isb();
    }
    }

    +/*
    + * Follow a read of the IAR with any HW maintenance that needs to happen prior
    + * to invoking the relevant IRQ handler. We must do two things:
    + *
    + * (1) Ensure instruction ordering between a read of IAR and subsequent
    + * instructions in the IRQ handler using an ISB.
    + *
    + * It is possible for the IAR to report an IRQ which was signalled *after*
    + * the CPU took an IRQ exception as multiple interrupts can race to be
    + * recognized by the GIC, earlier interrupts could be withdrawn, and/or
    + * later interrupts could be prioritized by the GIC.
    + *
    + * For devices which are tightly coupled to the CPU, such as PMUs, a
    + * context synchronization event is necessary to ensure that system
    + * register state is not stale, as these may have been indirectly written
    + * *after* exception entry.
    + *
    + * (2) Deactivate the interrupt when EOI mode 1 is in use.
    + */
    +static inline void gic_complete_ack(u32 irqnr)
    +{
    + if (static_branch_likely(&supports_deactivate_key))
    + write_gicreg(irqnr, ICC_EOIR1_EL1);
    +
    + isb();
    +}
    +
    static inline void gic_handle_nmi(u32 irqnr, struct pt_regs *regs)
    {
    bool irqs_enabled = interrupts_enabled(regs);
    @@ -652,10 +681,7 @@ static inline void gic_handle_nmi(u32 irqnr, struct pt_regs *regs)
    if (irqs_enabled)
    nmi_enter();

    - if (static_branch_likely(&supports_deactivate_key))
    - gic_write_eoir(irqnr);
    - else
    - isb()
    + gic_complete_ack(irqnr);

    /*
    * Leave the PSR.I bit set to prevent other NMIs to be
    @@ -726,10 +752,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
    gic_arch_enable_irqs();
    }

    - if (static_branch_likely(&supports_deactivate_key))
    - gic_write_eoir(irqnr);
    - else
    - isb();
    + gic_complete_ack(irqnr);

    if (generic_handle_domain_irq(gic_data.domain, irqnr)) {
    WARN_ONCE(true, "Unexpected interrupt received!\n");
    --
    2.35.1


    \
     
     \ /
      Last update: 2022-06-07 23:12    [W:3.700 / U:0.440 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site