diff mbox

[2/4] Improve cpu_idle when PM is disabled

Message ID 20170522055303.2025-3-npiggin@gmail.com
State Accepted
Headers show

Commit Message

Nicholas Piggin May 22, 2017, 5:53 a.m. UTC
Split cpu_idle() into cpu_idle_delay() and cpu_idle_job() rather than
requesting the idle type as a function argument. Have those functions
provide a default polling (non-PM) implentation which spin at the
lowest SMT priority.

This moves all the decrementer delay code into the CPU idle code rather
than the caller.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 core/cpu.c      | 60 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 core/init.c     |  4 ++--
 core/timebase.c | 28 ++++++++-------------------
 include/cpu.h   |  8 ++------
 4 files changed, 61 insertions(+), 39 deletions(-)
diff mbox

Patch

diff --git a/core/cpu.c b/core/cpu.c
index a03cf916..c4a4d29e 100644
--- a/core/cpu.c
+++ b/core/cpu.c
@@ -286,14 +286,10 @@  void cpu_process_jobs(void)
 	unlock(&cpu->job_lock);
 }
 
-static void cpu_idle_default(enum cpu_wake_cause wake_on __unused)
-{
-	/* Maybe do something better for simulators ? */
-	cpu_relax();
-	cpu_relax();
-	cpu_relax();
-	cpu_relax();
-}
+enum cpu_wake_cause {
+	cpu_wake_on_job,
+	cpu_wake_on_dec,
+};
 
 static void cpu_idle_p8(enum cpu_wake_cause wake_on)
 {
@@ -301,7 +297,7 @@  static void cpu_idle_p8(enum cpu_wake_cause wake_on)
 	struct cpu_thread *cpu = this_cpu();
 
 	if (!pm_enabled) {
-		cpu_idle_default(wake_on);
+		prlog_once(PR_DEBUG, "cpu_idle_p8 called pm disabled\n");
 		return;
 	}
 
@@ -373,18 +369,60 @@  void cpu_set_pm_enable(bool enabled)
 	}
 }
 
-void cpu_idle(enum cpu_wake_cause wake_on)
+static void cpu_idle_pm(enum cpu_wake_cause wake_on)
 {
 	switch(proc_gen) {
 	case proc_gen_p8:
 		cpu_idle_p8(wake_on);
 		break;
 	default:
-		cpu_idle_default(wake_on);
+		prlog_once(PR_DEBUG, "cpu_idle_pm called with bad processor type\n");
 		break;
 	}
 }
 
+void cpu_idle_job(void)
+{
+	if (pm_enabled) {
+		cpu_idle_pm(cpu_wake_on_job);
+	} else {
+		struct cpu_thread *cpu = this_cpu();
+
+		smt_lowest();
+		/* Check for jobs again */
+		while (!cpu_check_jobs(cpu))
+			barrier();
+		smt_medium();
+	}
+}
+
+void cpu_idle_delay(unsigned long delay, unsigned long min_pm)
+{
+	unsigned long now = mftb();
+	unsigned long end = now + delay;
+
+	if (pm_enabled && delay > min_pm) {
+		for (;;) {
+			if (delay >= 0x7fffffff)
+				delay = 0x7fffffff;
+			mtspr(SPR_DEC, delay);
+
+			cpu_idle_pm(cpu_wake_on_dec);
+
+			now = mftb();
+			if (tb_compare(now, end) == TB_AAFTERB)
+				break;
+
+			delay = end - now;
+		}
+	} else {
+		smt_lowest();
+		while (tb_compare(mftb(), end) != TB_AAFTERB)
+			barrier();
+		smt_medium();
+	}
+}
+
 void cpu_process_local_jobs(void)
 {
 	struct cpu_thread *cpu = first_available_cpu();
diff --git a/core/init.c b/core/init.c
index dce10fd6..8bd737a5 100644
--- a/core/init.c
+++ b/core/init.c
@@ -1068,9 +1068,9 @@  void __noreturn __secondary_cpu_entry(void)
 	/* Wait for work to do */
 	while(true) {
 		if (cpu_check_jobs(cpu))
-		    cpu_process_jobs();
+			cpu_process_jobs();
 		else
-		    cpu_idle(cpu_wake_on_job);
+			cpu_idle_job();
 	}
 }
 
diff --git a/core/timebase.c b/core/timebase.c
index a3c0fec5..ca961c39 100644
--- a/core/timebase.c
+++ b/core/timebase.c
@@ -24,8 +24,8 @@  unsigned long tb_hz = 512000000;
 
 static void time_wait_poll(unsigned long duration)
 {
-	unsigned long remaining = duration;
-	unsigned long end = mftb() + duration;
+	unsigned long now = mftb();
+	unsigned long end = now + duration;
 	unsigned long period = msecs_to_tb(5);
 
 	if (this_cpu()->tb_invalid) {
@@ -33,7 +33,9 @@  static void time_wait_poll(unsigned long duration)
 		return;
 	}
 
-	while (tb_compare(mftb(), end) != TB_AAFTERB) {
+	while (tb_compare(now, end) != TB_AAFTERB) {
+		unsigned long remaining = end - now;
+
 		/* Call pollers periodically but not continually to avoid
 		 * bouncing cachelines due to lock contention. */
 		if (remaining >= period) {
@@ -43,7 +45,7 @@  static void time_wait_poll(unsigned long duration)
 		} else
 			time_wait_nopoll(remaining);
 
-		cpu_relax();
+		now = mftb();
 	}
 }
 
@@ -64,28 +66,14 @@  void time_wait(unsigned long duration)
 
 void time_wait_nopoll(unsigned long duration)
 {
-	unsigned long end = mftb() + duration;
-	unsigned long min = usecs_to_tb(10);
+	unsigned long min_sleep = usecs_to_tb(10);
 
 	if (this_cpu()->tb_invalid) {
 		cpu_relax();
 		return;
 	}
 
-	for (;;) {
-		uint64_t delay, tb = mftb();
-
-		if (tb_compare(tb, end) == TB_AAFTERB)
-			break;
-		delay = end - tb;
-		if (delay >= 0x7fffffff)
-			delay = 0x7fffffff;
-		if (delay >= min) {
-			mtspr(SPR_DEC, delay);
-			cpu_idle(cpu_wake_on_dec);
-		} else
-			cpu_relax();
-	}
+	cpu_idle_delay(duration, min_sleep);
 }
 
 void time_wait_ms(unsigned long ms)
diff --git a/include/cpu.h b/include/cpu.h
index 938492e3..2d696994 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -43,11 +43,6 @@  enum cpu_thread_state {
 struct cpu_job;
 struct xive_cpu_state;
 
-enum cpu_wake_cause {
-	cpu_wake_on_job,
-	cpu_wake_on_dec,
-};
-
 struct cpu_thread {
 	uint32_t			pir;
 	uint32_t			server_no;
@@ -274,6 +269,7 @@  static inline void cpu_give_self_os(void)
 extern unsigned long __attrconst cpu_stack_bottom(unsigned int pir);
 extern unsigned long __attrconst cpu_stack_top(unsigned int pir);
 
-extern void cpu_idle(enum cpu_wake_cause wake_on);
+extern void cpu_idle_job(void);
+extern void cpu_idle_delay(unsigned long delay, unsigned long min_pm);
 
 #endif /* __CPU_H */