Shrikanth Hegde <[email protected]> writes:

> Add a flavour of interrupt disable which could be used in
> multiple places which also takes care of lockdep in both 32 and
> 64 bit archs.
>
> On PPC64:
> - Disable hard interrupts using hard_irq_disable. It takes care of
>   lockdep and tracing. local_irq_disable only alters softmask's state.
>
> On PPC32:
> - Interrupts are hard disabled with local_irq_disable. It also takes
>   care of lockdep and tacing.
>
> Subsequent patch uses this API in KVM code to avoid the warnings.
> Wider use of it is possible, but it will be taken subsequently.
>
> Suggested-by: Ritesh Harjani (IBM) <[email protected]>
> Tested-by: Christian Zigotzky <[email protected]>
> Reviewed-by: Mukesh Kumar Chaurasiya (IBM) <[email protected]>
> Signed-off-by: Shrikanth Hegde <[email protected]>
> ---
>  arch/powerpc/include/asm/hw_irq.h | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>
> diff --git a/arch/powerpc/include/asm/hw_irq.h 
> b/arch/powerpc/include/asm/hw_irq.h
> index b7eee6385ae5..075442366e1e 100644
> --- a/arch/powerpc/include/asm/hw_irq.h
> +++ b/arch/powerpc/include/asm/hw_irq.h
> @@ -516,6 +516,23 @@ static inline unsigned long mtmsr_isync_irqsafe(unsigned 
> long msr)
>  
>  #define ARCH_IRQ_INIT_FLAGS  IRQ_NOREQUEST
>  
> +/*
> + * Hard-disable interrupts while keeping the IRQ tracing state
> + * consistent.
> + *
> + * On PPC64, local_irq_disable() only soft-disables interrupts, leaving
> + * a window where an interrupt can be recorded in irq_happened before
> + * interrupts are hard-disabled.
> + *
> + * On PPC32, hard_irq_disable() does not update the IRQ tracing state, so
> + * use local_irq_disable().
> + */
> +#ifdef CONFIG_PPC64
> +#define ppc_hard_irq_disable()       hard_irq_disable()
> +#else
> +#define ppc_hard_irq_disable()       local_irq_disable()
> +#endif
> +

So I looked up the history of how did we end up here. And looks like we
will be papering over the problem, if we make the above change and add
yet another helper on PPC :)

Ideally, ppc32 shouldn't need a hard_irq_disable() definition at all.
As I understand it, the contract when this was all added was that, it is
meant for architectures which implement lazy IRQ disabling i.e.
    commit d04c56f73c30a5e593202ecfcf25ed43d42363a2
    Author: Paul Mackerras <[email protected]>
    Date:   Wed Oct 4 16:47:49 2006 +1000
        [POWERPC] Lazy interrupt disabling for 64-bit machines
 
...where local_irq_disable() only sets a soft mask. Code that really
wants interrupts disabled at the hardware level then calls
hard_irq_disable() as well.

This commit which defined a generic stub for hard_irq_disable() makes the
contract clear i.e.
    commit 2d3fbbb391e280724f7f7804ab00ff61cf1b6a4c
    Author: Benjamin Herrenschmidt <[email protected]>
    Date:   Thu May 10 22:22:46 2007 -0700

        Add hard_irq_disable()
which says:
    This patch implements a generic hard_irq_disable() function which is meant 
to
    be called -after- local_irq_disable() and ensures that interrupts are fully
    disabled on that CPU.  The default implementation is a nop, though powerpc
    does already provide an appropriate one.

And then the following even implements such a contract in stop_machine() i.e.:
    commit a12bb44471b819c7f33d8a83044e7116c124e3d5
    Author: Benjamin Herrenschmidt <[email protected]>
    Date:   Thu May 10 22:22:47 2007 -0700

        stop_machine() now uses hard_irq_disable

        Add a call to hard_irq_disable() to stop_machine so that we make sure 
IRQs are
        really disabled and not only lazy-disabled on archs like powerpc as 
some users
        of stop_machine() may rely on that.

        [[email protected]: build fix]
        Signed-off-by: Benjamin Herrenschmidt <[email protected]>
        Acked-by: Rusty Russell <[email protected]>
        Cc: Paul Mackerras <[email protected]>
        Signed-off-by: Andrew Morton <[email protected]>
        Signed-off-by: Linus Torvalds <[email protected]>

    diff --git a/kernel/stop_machine.c b/kernel/stop_machine.c
    index daabb74ee0bc..fcee2a8e6da3 100644
    --- a/kernel/stop_machine.c
    +++ b/kernel/stop_machine.c
    @@ -8,6 +8,8 @@
    #include <linux/sched.h>
    #include <linux/stop_machine.h>
    #include <linux/syscalls.h>
    +#include <linux/interrupt.h>
    +
    #include <asm/atomic.h>
    #include <asm/semaphore.h>
    #include <asm/uaccess.h>
    @@ -45,6 +47,7 @@ static int stopmachine(void *cpu)
            if (stopmachine_state == STOPMACHINE_DISABLE_IRQ 
                && !irqs_disabled) {
                local_irq_disable();
    +                   hard_irq_disable();
                irqs_disabled = 1;
                /* Ack: irqs disabled. */
                smp_mb(); /* Must read state first. */
    @@ -124,6 +127,7 @@ static int stop_machine(void)

        /* Make them disable irqs. */
        local_irq_disable();
    +   hard_irq_disable();
        stopmachine_set_state(STOPMACHINE_DISABLE_IRQ);

        return 0;


However, note that after introducing the lazy interrupt disabling in
2006, we saw build failures on ppc32, since hard_irq_disable()
definition didn't exist on that until then. This commit adds that
definition, which is not wrong, since the lazy interrupt disabling patch
really needed hard_irq_disable() to disable hardware interrupts. Hence
this commit defined it on PPC32 as well, as local_irq_disable() itself
i.e.

    commit 143db67af02cecab28032ec3a52e586b1433fe70
    Author: Michael Ellerman <[email protected]>
    Date:   Sun Nov 26 17:36:15 2006 +1100

        [POWERPC] Provide dummy hard_irq_enable/disable() for PPC32

        To allow arch/powerpc/kernel/crash.c to build on 32-bit we need a
        definition of hard_irq_disable(). 32-bit doesn't support the lazy
        interrupt disabling mechanism, so on 32-bit hard_irq_disable() is
        simply local_irq_disable(). Add a definition for hard_irq_enable()
        just for completeness.

        This allows (KEXEC=y && PPC32=y) to build again. Broken since
        d04c56f73c30a5e593202ecfcf25ed43d42363a2.

        Signed-off-by: Michael Ellerman <[email protected]>
        Signed-off-by: Paul Mackerras <[email protected]>

    diff --git a/include/asm-powerpc/hw_irq.h b/include/asm-powerpc/hw_irq.h
    index fd3f2a206271..d604863d72fb 100644
    --- a/include/asm-powerpc/hw_irq.h
    +++ b/include/asm-powerpc/hw_irq.h
    @@ -102,6 +102,9 @@ static inline void local_irq_save_ptr(unsigned long 
*flags)
    #define local_irq_save(flags)       local_irq_save_ptr(&flags)
    #define irqs_disabled()             ((mfmsr() & MSR_EE) == 0)

    +#define hard_irq_enable()  local_irq_enable()
    +#define hard_irq_disable() local_irq_disable()
    +
    #endif /* CONFIG_PPC64 */

    #define mask_irq(irq)                                               \


Note that the above is where we first defined hard_irq_disable() for
PPC32.
Ideally, IIUC, we don't really need this on PPC32 assuming the contract
is only for archs which uses lazy interrupt disabling (i.e. PPC64),
however, there were places where we ended up using hard_irq_disable()
assuming it disables hardware interrupts for both PPC32 and PPC64 code
and later maybe at a number of other places too.

Note that until this time, lockdep and IRQ tracing was not added. So it
wasn't a problem. However, later somewhere we ended up changing the
definition of hard_irq_disable(), to arch_local_irq_disable, maybe in
this...
   commit 5d38902c4838 ("powerpc: Add irqtrace support for 32-bit powerpc")


So IMO, in the longer term, I think we don't need hard_irq_disable() for
PPC32, and it should fallback to generic empty stub, after we fix all
those code paths and use appropriate helpers there. However, that might
need careful inspection of a lot of code paths.


Hence, meanwhile, I think instead of defining another helper like
ppc_hard_irq_disable(), (which was stupid on my part to even suggest w/o
looking at this history), we should change hard_irq_disable() definition
back to local_irq_disable() for PPC32, which anyways takes care of
trace_hardirqs_off().

Therefore, I think we need the following 2 diffs - which should fix both
of the reported issues as well. And this way, we don't need another hard
irq disabling helper on PPC.

DIFF(1)

    diff --git a/arch/powerpc/include/asm/hw_irq.h 
b/arch/powerpc/include/asm/hw_irq.h
    index b7eee6385ae5..ddb850e62abd 100644
    --- a/arch/powerpc/include/asm/hw_irq.h
    +++ b/arch/powerpc/include/asm/hw_irq.h
    @@ -464,7 +464,7 @@ static inline bool arch_irqs_disabled(void)
            return arch_irqs_disabled_flags(arch_local_save_flags());
     }
    
    -#define hard_irq_disable()             arch_local_irq_disable()
    +#define hard_irq_disable()             local_irq_disable()
    
     static inline bool regs_irqs_disabled(struct pt_regs *regs)
     {


and...

DIFF(2)

    diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
    index 13ad4cf5fa71..543479fc9630 100644
    --- a/arch/powerpc/kvm/booke.c
    +++ b/arch/powerpc/kvm/booke.c
    @@ -722,7 +722,6 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
            if (vcpu->arch.shared->msr & MSR_WE) {
                    local_irq_enable();
                    kvm_vcpu_halt(vcpu);
    -               local_irq_disable();
                    hard_irq_disable();

                    kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
    diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
    index 9194cf492d1c..f4db7dea57b3 100644
    --- a/arch/powerpc/kvm/powerpc.c
    +++ b/arch/powerpc/kvm/powerpc.c
    @@ -81,12 +81,6 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
            int r;

            WARN_ON(irqs_disabled());
    -       /*
    -        * local_irq_disable() first: on 32-bit, hard_irq_disable() alone 
is a
    -        * raw MSR[EE] clear that bypasses the lockdep/irq-tracing state, 
and
    -        * the xfer_to_guest_mode helpers assert IRQs are seen as disabled.
    -        */
    -       local_irq_disable();
            hard_irq_disable();

            while (true) {
    @@ -101,7 +95,6 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
                            */
                            local_irq_enable();
                            r = kvm_xfer_to_guest_mode_handle_work(vcpu);
    -                       local_irq_disable();
                            hard_irq_disable();
                            if (r) {
                                    /*
    @@ -134,7 +127,6 @@ int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu)
                            local_irq_enable();
                            trace_kvm_check_requests(vcpu);
                            r = kvmppc_core_check_requests(vcpu);
    -                       local_irq_disable();
                            hard_irq_disable();
                            if (r > 0)
                                    continue;


@Shrikanth, @Mukesh, your thoughts?
If this looks good to you too, would you like to take this forward?

And, if I am not wrong, with the above 2 diffs, we don't need this patch
either [1].

[1]: [PATCH] powerpc/entry: Trace the IRQ disable before irqentry_exit() on 
32-bit
[1]: 
https://lore.kernel.org/linuxppc-dev/a14571be1897955f43d9994f0e093de3ada6ecde.1793447880.git.ritesh.l...@gmail.com/



-ritesh

Reply via email to