powerpc/powernv/idle: Restore AMR/UAMOR/AMOR/IAMR after idle [Linux 5.2]

powerpc/powernv/idle: Restore AMR/UAMOR/AMOR/IAMR after idle [Linux 5.2]

This Linux kernel change "powerpc/powernv/idle: Restore AMR/UAMOR/AMOR/IAMR after idle" is included in the Linux 5.2 release. This change is authored by Michael Ellerman <mpe [at] ellerman.id.au> on Tue Apr 30 14:28:17 2019 +1000. The commit for this change in Linux stable tree is e9cef01 (patch).

powerpc/powernv/idle: Restore AMR/UAMOR/AMOR/IAMR after idle

This is an implementation of commits 53a712bae5dd
("powerpc/powernv/idle: Restore AMR/UAMOR/AMOR after idle") and
a3f3072db6ca ("powerpc/powernv/idle: Restore IAMR after idle") using
the new C-based idle code.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
[mpe: Extract from Nick's patch]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

There are 52 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 arch/powerpc/platforms/powernv/idle.c | 52 +++++++++++++++++++++++++++++++----
 1 file changed, 46 insertions(+), 6 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
index 87f5f4a..c9133f7 100644
--- a/arch/powerpc/platforms/powernv/idle.c
+++ b/arch/powerpc/platforms/powernv/idle.c
@@ -296,7 +296,6 @@ struct p7_sprs {
    /* per subcore */
    u64 sdr1;
    u64 rpr;
-   u64 amor;

    /* per thread */
    u64 lpcr;
@@ -306,6 +305,12 @@ struct p7_sprs {
    u64 spurr;
    u64 dscr;
    u64 wort;
+
+   /* per thread SPRs that get lost in shallow states */
+   u64 amr;
+   u64 iamr;
+   u64 amor;
+   u64 uamor;
 };

 static unsigned long power7_idle_insn(unsigned long type)
@@ -342,7 +347,6 @@ static unsigned long power7_idle_insn(unsigned long type)

            sprs.sdr1   = mfspr(SPRN_SDR1);
            sprs.rpr    = mfspr(SPRN_RPR);
-           sprs.amor   = mfspr(SPRN_AMOR);

            sprs.lpcr   = mfspr(SPRN_LPCR);
            if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
@@ -374,6 +378,13 @@ static unsigned long power7_idle_insn(unsigned long type)
        atomic_unlock_thread_idle();
    }

+   if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+       sprs.amr    = mfspr(SPRN_AMR);
+       sprs.iamr   = mfspr(SPRN_IAMR);
+       sprs.amor   = mfspr(SPRN_AMOR);
+       sprs.uamor  = mfspr(SPRN_UAMOR);
+   }
+
    local_paca->thread_idle_state = type;
    srr1 = isa206_idle_insn_mayloss(type);      /* go idle */
    local_paca->thread_idle_state = PNV_THREAD_RUNNING;
@@ -381,6 +392,19 @@ static unsigned long power7_idle_insn(unsigned long type)
    WARN_ON_ONCE(!srr1);
    WARN_ON_ONCE(mfmsr() & (MSR_IR|MSR_DR));

+   if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+       if ((srr1 & SRR1_WAKESTATE) != SRR1_WS_NOLOSS) {
+           /*
+            * We don't need an isync after the mtsprs here because
+            * the upcoming mtmsrd is execution synchronizing.
+            */
+           mtspr(SPRN_AMR,     sprs.amr);
+           mtspr(SPRN_IAMR,    sprs.iamr);
+           mtspr(SPRN_AMOR,    sprs.amor);
+           mtspr(SPRN_UAMOR,   sprs.uamor);
+       }
+   }
+
    if (unlikely((srr1 & SRR1_WAKEMASK_P8) == SRR1_WAKEHMI))
        hmi_exception_realmode(NULL);

@@ -444,7 +468,6 @@ static unsigned long power7_idle_insn(unsigned long type)
    /* Per-subcore SPRs */
    mtspr(SPRN_SDR1,    sprs.sdr1);
    mtspr(SPRN_RPR,     sprs.rpr);
-   mtspr(SPRN_AMOR,    sprs.amor);

 subcore_woken:
    /*
@@ -560,7 +583,6 @@ struct p9_sprs {
    u64 rpr;
    u64 tscr;
    u64 ldbar;
-   u64 amor;

    /* per thread */
    u64 lpcr;
@@ -576,6 +598,12 @@ struct p9_sprs {
    u32 mmcr0;
    u32 mmcr1;
    u64 mmcr2;
+
+   /* per thread SPRs that get lost in shallow states */
+   u64 amr;
+   u64 iamr;
+   u64 amor;
+   u64 uamor;
 };

 static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on)
@@ -652,13 +680,17 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on)
        sprs.rpr    = mfspr(SPRN_RPR);
        sprs.tscr   = mfspr(SPRN_TSCR);
        sprs.ldbar  = mfspr(SPRN_LDBAR);
-       sprs.amor   = mfspr(SPRN_AMOR);

        sprs_saved = true;

        atomic_start_thread_idle();
    }

+   sprs.amr    = mfspr(SPRN_AMR);
+   sprs.iamr   = mfspr(SPRN_IAMR);
+   sprs.amor   = mfspr(SPRN_AMOR);
+   sprs.uamor  = mfspr(SPRN_UAMOR);
+
    srr1 = isa300_idle_stop_mayloss(psscr);     /* go idle */

 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
@@ -674,6 +706,15 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on)
        unsigned long mmcra;

        /*
+        * We don't need an isync after the mtsprs here because the
+        * upcoming mtmsrd is execution synchronizing.
+        */
+       mtspr(SPRN_AMR,     sprs.amr);
+       mtspr(SPRN_IAMR,    sprs.iamr);
+       mtspr(SPRN_AMOR,    sprs.amor);
+       mtspr(SPRN_UAMOR,   sprs.uamor);
+
+       /*
         * Workaround for POWER9 DD2.0, if we lost resources, the ERAT
         * might have been corrupted and needs flushing. We also need
         * to reload MMCR0 (see mmcr0 comment above).
@@ -722,7 +763,6 @@ static unsigned long power9_idle_stop(unsigned long psscr, bool mmu_on)
    mtspr(SPRN_RPR,     sprs.rpr);
    mtspr(SPRN_TSCR,    sprs.tscr);
    mtspr(SPRN_LDBAR,   sprs.ldbar);
-   mtspr(SPRN_AMOR,    sprs.amor);

    if (pls >= pnv_first_tb_loss_level) {
        /* TB loss */

Leave a Reply

Your email address will not be published. Required fields are marked *