Subject: Re: Possible bug in commit f3b3f28493d9

From: Gautham R Shenoy <[email-masked]>
Date: Fri, 15 Sep 2017 14:10:02 +0530

Hi Paul,

On Thu, Sep 14, 2017 at 02:57:07PM +1000, Paul Mackerras wrote:
> Commit f3b3f28493d9 ("powerpc/powernv/idle: Don't override
> default/deepest directly in kernel", 2017-03-22) made the following
> change in pnv_cpu_offline() in arch/powerpc/platforms/powernv/idle.c:
> 
> -	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
> +	if (cpu_has_feature(CPU_FTR_ARCH_300) && deepest_stop_found) {
>  		srr1 = power9_idle_stop(pnv_deepest_stop_psscr_val,
>  					pnv_deepest_stop_psscr_mask);
>  	} else if (idle_states & OPAL_PM_WINKLE_ENABLED) {
>  		srr1 = power7_winkle();
>

This patch follows an earlier patch with commit 900612315788
("powerpc/powernv/smp: Add busy-wait loop as fall back for
CPU-Hotplug") to ensure that when platform idle states aren't
available, the offlined CPUs spin in a busy while loop.

> Which seems to be saying that previously, on POWER9 we would always
> call power9_idle_stop(), but now we might possibly call
> power7_idle_insn() to do a nap or sleep or rvwinkle instruction.
>

On POWER9, none of the platform idle states (stop states) exposed by
the skiboot have OPAL_PM_[NAP/SLEEP/WINKLE]_ENABLED set in their
respective flags. Hence we will never enter those "else if"
conditions.

> Is this really what was meant?  Nap, sleep and rvwinkle are illegal
> instructions on POWER9.  It looks to me as if that statement needs to
> be restructured to do what the commit description says, which is to
> call power9_idle_stop if we have found a valid stop state, and
> otherwise to poll.
> At present we are relying on idle_states not not having any of the
> nap/sleep/winkle bits set in it.  Is that guaranteed on POWER9?  If so
> it is at least deserving of a comment.

How about the following patch

---------------------------x8---------x8---------------------------------
>>From a17e4f71bfc9d208c45335acb47fc2b3a9f61923 Mon Sep 17 00:00:00 2001
From: "Gautham R. Shenoy" <[email-masked]>
Date: Fri, 15 Sep 2017 13:32:04 +0530
Subject: [PATCH] powerpc/powernv/idle: Restructure pnv_cpu_offline for readability

The current code for handling CPU offline will call a function to put
a CPU in a stop state if this is a POWER9 CPU and if a suitable stop
state is found. If one of these conditions fails, it checks for the
OPAL flags for the idle state to see if winkle, fastsleep, nap are
available (which shouldn't be available on POWER9) and finally put the
CPU into a busy while loop.

This code gives an impression that we are exploring the possibility of
putting the CPUs into fastsleep/nap/winkle on POWER9 even though that
is not possible and is illegal.

Hence rewrite the code to seperately handle the offline for POWER9 and
pre-POWER9 CPUs.

This patch introduces no functional change. Just restructures the code
for improved readability.

Signed-off-by: Gautham R. Shenoy <[email-masked]>
---
 arch/powerpc/platforms/powernv/idle.c | 70 +++++++++++++++++++++++------------
 1 file changed, 47 insertions(+), 23 deletions(-)

diff --git a/arch/powerpc/platforms/powernv/idle.c b/arch/powerpc/platforms/powernv/idle.c
index a553aee..67b5475 100644
--- a/arch/powerpc/platforms/powernv/idle.c
+++ b/arch/powerpc/platforms/powernv/idle.c
@@ -380,6 +380,29 @@ void power9_idle_type(unsigned long stop_psscr_val,
 }
 
 /*
+ * offline_idle_loop: Offlined CPUs will busy-idle on platforms that
+ *	don't have any platform idle state enabled.
+ *
+ * @cpu: The cpu that is offlined.
+ *
+ * Returns 0 since the srr1 wasn't lost as we didn't execute any
+ * platform idle.
+ *
+ */
+unsigned long offline_idle_loop(unsigned int cpu)
+{
+	/* This is the fallback method. We emulate snooze */
+	while (!generic_check_cpu_restart(cpu)) {
+		HMT_low();
+		HMT_very_low();
+	}
+
+	HMT_medium();
+
+	return 0;
+}
+
+/*
  * Used for ppc_md.power_save which needs a function with no parameters
  */
 void power9_idle(void)
@@ -391,6 +414,9 @@ void power9_idle(void)
 /*
  * pnv_cpu_offline: A function that puts the CPU into the deepest
  * available platform idle state on a CPU-Offline.
+ * If no platform idle state is available, the CPU busy waits.
+ * (see offline_idle_loop())
+ *
  * interrupts hard disabled and no lazy irq pending.
  */
 unsigned long pnv_cpu_offline(unsigned int cpu)
@@ -400,32 +426,30 @@ unsigned long pnv_cpu_offline(unsigned int cpu)
 
 	__ppc64_runlatch_off();
 
-	if (cpu_has_feature(CPU_FTR_ARCH_300) && deepest_stop_found) {
-		unsigned long psscr;
-
-		psscr = mfspr(SPRN_PSSCR);
-		psscr = (psscr & ~pnv_deepest_stop_psscr_mask) |
-						pnv_deepest_stop_psscr_val;
-		srr1 = power9_idle_stop(psscr);
-
-	} else if ((idle_states & OPAL_PM_WINKLE_ENABLED) &&
-		   (idle_states & OPAL_PM_LOSE_FULL_CONTEXT)) {
-		srr1 = power7_idle_insn(PNV_THREAD_WINKLE);
-	} else if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
-		   (idle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
-		srr1 = power7_idle_insn(PNV_THREAD_SLEEP);
-	} else if (idle_states & OPAL_PM_NAP_ENABLED) {
-		srr1 = power7_idle_insn(PNV_THREAD_NAP);
+	if (cpu_has_feature(CPU_FTR_ARCH_300)) {
+		if (deepest_stop_found) {
+			unsigned long psscr;
+
+			psscr = mfspr(SPRN_PSSCR);
+			psscr = (psscr & ~pnv_deepest_stop_psscr_mask) |
+				pnv_deepest_stop_psscr_val;
+			srr1 = power9_idle_stop(psscr);
+		} else {
+			srr1 = offline_idle_loop(cpu);
+		}
 	} else {
-		/* This is the fallback method. We emulate snooze */
-		while (!generic_check_cpu_restart(cpu)) {
-			HMT_low();
-			HMT_very_low();
+		if ((idle_states & OPAL_PM_WINKLE_ENABLED) &&
+		    (idle_states & OPAL_PM_LOSE_FULL_CONTEXT)) {
+			srr1 = power7_idle_insn(PNV_THREAD_WINKLE);
+		} else if ((idle_states & OPAL_PM_SLEEP_ENABLED) ||
+			 (idle_states & OPAL_PM_SLEEP_ENABLED_ER1)) {
+			srr1 = power7_idle_insn(PNV_THREAD_SLEEP);
+		} else if (idle_states & OPAL_PM_NAP_ENABLED) {
+			srr1 = power7_idle_insn(PNV_THREAD_NAP);
+		} else {
+			srr1 = offline_idle_loop(cpu);
 		}
-		srr1 = 0;
-		HMT_medium();
 	}
-
 	__ppc64_runlatch_on();
 
 	return srr1;
-- 
1.9.4

> Paul.