]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/commitdiff
powerpc/powernv: Recover correct PACA on wakeup from a stop on P9 DD1
authorGautham R. Shenoy <ego@linux.vnet.ibm.com>
Wed, 17 May 2017 10:51:11 +0000 (06:51 -0400)
committerStefan Bader <stefan.bader@canonical.com>
Tue, 20 Jun 2017 08:45:46 +0000 (10:45 +0200)
BugLink: http://bugs.launchpad.net/bugs/1685792
POWER9 DD1.0 hardware has a bug where the SPRs of a thread waking up
from stop 0,1,2 with ESL=1 can endup being misplaced in the core. Thus
the HSPRG0 of a thread waking up from can contain the paca pointer of
its sibling.

This patch implements a context recovery framework within threads of a
core, by provisioning space in paca_struct for saving every sibling
threads's paca pointers. Basically, we should be able to arrive at the
right paca pointer from any of the thread's existing paca pointer.

At bootup, during powernv idle-init, we save the paca address of every
CPU in each one its siblings paca_struct in the slot corresponding to
this CPU's index in the core.

On wakeup from a stop, the thread will determine its index in the core
from the TIR register and recover its PACA pointer by indexing into
the correct slot in the provisioned space in the current PACA.

Furthermore, ensure that the NVGPRs are restored from the stack on the
way out by setting the NAPSTATELOST in paca.

[Changelog written with inputs from svaidy@linux.vnet.ibm.com]
Signed-off-by: Gautham R. Shenoy <ego@linux.vnet.ibm.com>
Reviewed-by: Nicholas Piggin <npiggin@gmail.com>
[mpe: Call it a bug]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
(backported from commit 17ed4c8f81da2bf340d33a8c875f4d6b1dfd9398)
Signed-off-by: Joseph Salisbury <joseph.salisbury@canonical.com>
Acked-by: Colin Ian King <colin.king@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
arch/powerpc/include/asm/paca.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/idle_book3s.S
arch/powerpc/platforms/powernv/idle.c

index 026082b3c5ac9f8d194161eb0efeff78b369caac..be9ab143978c24f8879fdf1a281be4635f52e196 100644 (file)
@@ -175,6 +175,11 @@ struct paca_struct {
        u8 thread_mask;
        /* Mask to denote subcore sibling threads */
        u8 subcore_sibling_mask;
+       /*
+        * Pointer to an array which contains pointer
+        * to the sibling threads' paca.
+        */
+       struct paca_struct **thread_sibling_pacas;
 #endif
 
 #ifdef CONFIG_PPC_BOOK3S_64
index 708880fb3b2375ac4a3feffceb02f0e9be461e83..75cecc9cdb49864cef6ed431b539d29fa0e70ab9 100644 (file)
@@ -755,6 +755,8 @@ int main(void)
                        offsetof(struct paca_struct, thread_mask));
        DEFINE(PACA_SUBCORE_SIBLING_MASK,
                        offsetof(struct paca_struct, subcore_sibling_mask));
+       DEFINE(PACA_SIBLING_PACA_PTRS,
+               offsetof(struct paca_struct, thread_sibling_pacas));
 #endif
 
        DEFINE(PPC_DBELL_SERVER, PPC_DBELL_SERVER);
index 6fd08219248db7485a6d5c8227dee83664d29b38..67ba6a66fed5c46cd85708d09438f687d65866e3 100644 (file)
@@ -375,6 +375,46 @@ _GLOBAL(power9_idle_stop)
        li      r4,1
        b       pnv_powersave_common
        /* No return */
+
+
+/*
+ * On waking up from stop 0,1,2 with ESL=1 on POWER9 DD1,
+ * HSPRG0 will be set to the HSPRG0 value of one of the
+ * threads in this core. Thus the value we have in r13
+ * may not be this thread's paca pointer.
+ *
+ * Fortunately, the TIR remains invariant. Since this thread's
+ * paca pointer is recorded in all its sibling's paca, we can
+ * correctly recover this thread's paca pointer if we
+ * know the index of this thread in the core.
+ *
+ * This index can be obtained from the TIR.
+ *
+ * i.e, thread's position in the core = TIR.
+ * If this value is i, then this thread's paca is
+ * paca->thread_sibling_pacas[i].
+ */
+power9_dd1_recover_paca:
+       mfspr   r4, SPRN_TIR
+       /*
+        * Since each entry in thread_sibling_pacas is 8 bytes
+        * we need to left-shift by 3 bits. Thus r4 = i * 8
+        */
+       sldi    r4, r4, 3
+       /* Get &paca->thread_sibling_pacas[0] in r5 */
+       ld      r5, PACA_SIBLING_PACA_PTRS(r13)
+       /* Load paca->thread_sibling_pacas[i] into r13 */
+       ldx     r13, r4, r5
+       SET_PACA(r13)
+       ld      r2, PACATOC(r13)
+       /*
+        * Indicate that we have lost NVGPR state
+        * which needs to be restored from the stack.
+        */
+       li      r3, 1
+       stb     r0,PACA_NAPSTATELOST(r13)
+       blr
+
 /*
  * Called from reset vector. Check whether we have woken up with
  * hypervisor state loss. If yes, restore hypervisor state and return
@@ -385,7 +425,13 @@ _GLOBAL(power9_idle_stop)
  */
 _GLOBAL(pnv_restore_hyp_resource)
 BEGIN_FTR_SECTION
-       ld      r2,PACATOC(r13);
+BEGIN_FTR_SECTION_NESTED(70)
+       mflr    r6
+       bl      power9_dd1_recover_paca
+       mtlr    r6
+FTR_SECTION_ELSE_NESTED(70)
+       ld      r2, PACATOC(r13)
+ALT_FTR_SECTION_END_NESTED_IFSET(CPU_FTR_POWER9_DD1, 70)
        /*
         * POWER ISA 3. Use PSSCR to determine if we
         * are waking up from deep idle state
index 63ade787aec49f22c9801ceee9c95f65702fa6e4..b369e39aa39278a320ee57ca8ab1e37fd916c244 100644 (file)
@@ -122,9 +122,12 @@ static void pnv_alloc_idle_core_states(void)
        for (i = 0; i < nr_cores; i++) {
                int first_cpu = i * threads_per_core;
                int node = cpu_to_node(first_cpu);
+               size_t paca_ptr_array_size;
 
                core_idle_state = kmalloc_node(sizeof(u32), GFP_KERNEL, node);
                *core_idle_state = PNV_CORE_IDLE_THREAD_BITS;
+               paca_ptr_array_size = (threads_per_core *
+                                      sizeof(struct paca_struct *));
 
                for (j = 0; j < threads_per_core; j++) {
                        int cpu = first_cpu + j;
@@ -132,6 +135,11 @@ static void pnv_alloc_idle_core_states(void)
                        paca[cpu].core_idle_state_ptr = core_idle_state;
                        paca[cpu].thread_idle_state = PNV_THREAD_RUNNING;
                        paca[cpu].thread_mask = 1 << j;
+                       if (!cpu_has_feature(CPU_FTR_POWER9_DD1))
+                               continue;
+                       paca[cpu].thread_sibling_pacas =
+                               kmalloc_node(paca_ptr_array_size,
+                                            GFP_KERNEL, node);
                }
        }
 
@@ -560,6 +568,28 @@ static int __init pnv_init_idle_states(void)
 
        pnv_alloc_idle_core_states();
 
+       /*
+        * For each CPU, record its PACA address in each of it's
+        * sibling thread's PACA at the slot corresponding to this
+        * CPU's index in the core.
+        */
+       if (cpu_has_feature(CPU_FTR_POWER9_DD1)) {
+               int cpu;
+
+               pr_info("powernv: idle: Saving PACA pointers of all CPUs in their thread sibling PACA\n");
+               for_each_possible_cpu(cpu) {
+                       int base_cpu = cpu_first_thread_sibling(cpu);
+                       int idx = cpu_thread_in_core(cpu);
+                       int i;
+
+                       for (i = 0; i < threads_per_core; i++) {
+                               int j = base_cpu + i;
+
+                               paca[j].thread_sibling_pacas[idx] = &paca[cpu];
+                       }
+               }
+       }
+
        if (supported_cpuidle_states & OPAL_PM_NAP_ENABLED)
                ppc_md.power_save = power7_idle;