linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] PM / suspend: Suspend-to-idle core optimization
@ 2017-07-21  0:06 Rafael J. Wysocki
  2017-07-21  0:07 ` [PATCH 1/4] PM / s2idle: Rearrange the main suspend-to-idle loop Rafael J. Wysocki
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2017-07-21  0:06 UTC (permalink / raw)
  To: Linux PM; +Cc: LKML

Hi,

This series is on top of the one I sent a couple of days ago:

http://marc.info/?l=linux-pm&m=150042550025820&w=2

but it is mostly independent of that one.

Basically, it restores the pm_wakeup_pending() check in __suspend_device_noirq()
removed recently, which may avoid unnecessary device ping-pong if there's a
wakeup event during "noirq" suspend, and makes suspend-to-idle take that check
into account (it actually matters more for suspend-to-idle, because it may carry out
"noirq" phases of device suspend/resume for multiple times in one cycle).

It also makes debug messages from the core device suspend/resume code look
better on failures (or when operations are aborted by pending wakeup events).

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4] PM / s2idle: Rearrange the main suspend-to-idle loop
  2017-07-21  0:06 [PATCH 0/4] PM / suspend: Suspend-to-idle core optimization Rafael J. Wysocki
@ 2017-07-21  0:07 ` Rafael J. Wysocki
  2017-07-21  0:09 ` [PATCH 2/4] PM / core: Split dpm_suspend_noirq() and dpm_resume_noirq() Rafael J. Wysocki
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2017-07-21  0:07 UTC (permalink / raw)
  To: Linux PM; +Cc: LKML

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

As a preparation for subsequent changes, rearrange the core
suspend-to-idle code by moving the initial invocation of
dpm_suspend_noirq() into s2idle_loop().

This also causes debug messages from that code to appear in
a less confusing order.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 kernel/power/power.h   |    4 ++++
 kernel/power/suspend.c |   26 +++++++++++++-------------
 2 files changed, 17 insertions(+), 13 deletions(-)

Index: linux-pm/kernel/power/suspend.c
===================================================================
--- linux-pm.orig/kernel/power/suspend.c
+++ linux-pm/kernel/power/suspend.c
@@ -106,7 +106,13 @@ static void s2idle_loop(void)
 {
 	pm_pr_dbg("suspend-to-idle\n");
 
-	do {
+	while (!dpm_suspend_noirq(PMSG_SUSPEND)) {
+		/*
+		 * Suspend-to-idle equals
+		 * frozen processes + suspended devices + idle processors.
+		 * Thus freeze_enter() should be called right after
+		 * all devices have been suspended.
+		 */
 		freeze_enter();
 
 		if (freeze_ops && freeze_ops->wake)
@@ -120,7 +126,7 @@ static void s2idle_loop(void)
 			break;
 
 		pm_wakeup_clear(false);
-	} while (!dpm_suspend_noirq(PMSG_SUSPEND));
+	}
 
 	pm_pr_dbg("resume from suspend-to-idle\n");
 }
@@ -377,6 +383,11 @@ static int suspend_enter(suspend_state_t
 	if (error)
 		goto Devices_early_resume;
 
+	if (state == PM_SUSPEND_FREEZE && pm_test_level != TEST_PLATFORM) {
+		s2idle_loop();
+		goto Platform_early_resume;
+	}
+
 	error = dpm_suspend_noirq(PMSG_SUSPEND);
 	if (error) {
 		pr_err("PM: noirq suspend of devices failed\n");
@@ -389,17 +400,6 @@ static int suspend_enter(suspend_state_t
 	if (suspend_test(TEST_PLATFORM))
 		goto Platform_wake;
 
-	/*
-	 * PM_SUSPEND_FREEZE equals
-	 * frozen processes + suspended devices + idle processors.
-	 * Thus we should invoke freeze_enter() soon after
-	 * all the devices are suspended.
-	 */
-	if (state == PM_SUSPEND_FREEZE) {
-		s2idle_loop();
-		goto Platform_early_resume;
-	}
-
 	error = disable_nonboot_cpus();
 	if (error || suspend_test(TEST_CPUS))
 		goto Enable_cpus;
Index: linux-pm/kernel/power/power.h
===================================================================
--- linux-pm.orig/kernel/power/power.h
+++ linux-pm/kernel/power/power.h
@@ -245,7 +245,11 @@ enum {
 #define TEST_FIRST	TEST_NONE
 #define TEST_MAX	(__TEST_AFTER_LAST - 1)
 
+#ifdef CONFIG_PM_DEBUG
 extern int pm_test_level;
+#else
+#define pm_test_level	(TEST_NONE)
+#endif
 
 #ifdef CONFIG_SUSPEND_FREEZER
 static inline int suspend_freeze_processes(void)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/4] PM / core: Split dpm_suspend_noirq() and dpm_resume_noirq()
  2017-07-21  0:06 [PATCH 0/4] PM / suspend: Suspend-to-idle core optimization Rafael J. Wysocki
  2017-07-21  0:07 ` [PATCH 1/4] PM / s2idle: Rearrange the main suspend-to-idle loop Rafael J. Wysocki
@ 2017-07-21  0:09 ` Rafael J. Wysocki
  2017-07-21  0:10 ` [PATCH 3/4] PM / core: Add error argument to dpm_show_time() Rafael J. Wysocki
  2017-07-21  0:12 ` [PATCH 4/4] PM / sleep: Check pm_wakeup_pending() in __device_suspend_noirq() Rafael J. Wysocki
  3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2017-07-21  0:09 UTC (permalink / raw)
  To: Linux PM; +Cc: LKML

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Put the device interrupts disabling and enabling as well as
cpuidle_pause() and cpuidle_resume() called during the "noirq"
stages of system suspend into separate functions to allow the
core suspend-to-idle code to be optimized (later).

The only functional difference this makes is that debug facilities
and diagnostic tools will not include the above operations into the
"noirq" device suspend/resume duration measurements.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/base/power/main.c |   67 +++++++++++++++++++++++++++++++---------------
 include/linux/pm.h        |    4 ++
 2 files changed, 50 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/base/power/main.c
===================================================================
--- linux-pm.orig/drivers/base/power/main.c
+++ linux-pm/drivers/base/power/main.c
@@ -598,14 +598,7 @@ static void async_resume_noirq(void *dat
 	put_device(dev);
 }
 
-/**
- * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
- * @state: PM transition of the system being carried out.
- *
- * Call the "noirq" resume handlers for all devices in dpm_noirq_list and
- * enable device drivers to receive interrupts.
- */
-void dpm_resume_noirq(pm_message_t state)
+void dpm_noirq_resume_devices(pm_message_t state)
 {
 	struct device *dev;
 	ktime_t starttime = ktime_get();
@@ -651,10 +644,27 @@ void dpm_resume_noirq(pm_message_t state
 	mutex_unlock(&dpm_list_mtx);
 	async_synchronize_full();
 	dpm_show_time(starttime, state, "noirq");
+	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
+}
+
+void dpm_noirq_end(void)
+{
 	resume_device_irqs();
 	device_wakeup_disarm_wake_irqs();
 	cpuidle_resume();
-	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
+}
+
+/**
+ * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
+ * @state: PM transition of the system being carried out.
+ *
+ * Invoke the "noirq" resume callbacks for all devices in dpm_noirq_list and
+ * allow device drivers' interrupt handlers to be called.
+ */
+void dpm_resume_noirq(pm_message_t state)
+{
+	dpm_noirq_resume_devices(state);
+	dpm_noirq_end();
 }
 
 /**
@@ -1154,22 +1164,19 @@ static int device_suspend_noirq(struct d
 	return __device_suspend_noirq(dev, pm_transition, false);
 }
 
-/**
- * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
- * @state: PM transition of the system being carried out.
- *
- * Prevent device drivers from receiving interrupts and call the "noirq" suspend
- * handlers for all non-sysdev devices.
- */
-int dpm_suspend_noirq(pm_message_t state)
+void dpm_noirq_begin(void)
+{
+	cpuidle_pause();
+	device_wakeup_arm_wake_irqs();
+	suspend_device_irqs();
+}
+
+int dpm_noirq_suspend_devices(pm_message_t state)
 {
 	ktime_t starttime = ktime_get();
 	int error = 0;
 
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
-	cpuidle_pause();
-	device_wakeup_arm_wake_irqs();
-	suspend_device_irqs();
 	mutex_lock(&dpm_list_mtx);
 	pm_transition = state;
 	async_error = 0;
@@ -1204,7 +1211,6 @@ int dpm_suspend_noirq(pm_message_t state
 	if (error) {
 		suspend_stats.failed_suspend_noirq++;
 		dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
-		dpm_resume_noirq(resume_event(state));
 	} else {
 		dpm_show_time(starttime, state, "noirq");
 	}
@@ -1213,6 +1219,25 @@ int dpm_suspend_noirq(pm_message_t state
 }
 
 /**
+ * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
+ * @state: PM transition of the system being carried out.
+ *
+ * Prevent device drivers' interrupt handlers from being called and invoke
+ * "noirq" suspend callbacks for all non-sysdev devices.
+ */
+int dpm_suspend_noirq(pm_message_t state)
+{
+	int ret;
+
+	dpm_noirq_begin();
+	ret = dpm_noirq_suspend_devices(state);
+	if (ret)
+		dpm_resume_noirq(resume_event(state));
+
+	return ret;
+}
+
+/**
  * device_suspend_late - Execute a "late suspend" callback for given device.
  * @dev: Device to handle.
  * @state: PM transition of the system being carried out.
Index: linux-pm/include/linux/pm.h
===================================================================
--- linux-pm.orig/include/linux/pm.h
+++ linux-pm/include/linux/pm.h
@@ -689,6 +689,8 @@ struct dev_pm_domain {
 extern void device_pm_lock(void);
 extern void dpm_resume_start(pm_message_t state);
 extern void dpm_resume_end(pm_message_t state);
+extern void dpm_noirq_resume_devices(pm_message_t state);
+extern void dpm_noirq_end(void);
 extern void dpm_resume_noirq(pm_message_t state);
 extern void dpm_resume_early(pm_message_t state);
 extern void dpm_resume(pm_message_t state);
@@ -697,6 +699,8 @@ extern void dpm_complete(pm_message_t st
 extern void device_pm_unlock(void);
 extern int dpm_suspend_end(pm_message_t state);
 extern int dpm_suspend_start(pm_message_t state);
+extern void dpm_noirq_begin(void);
+extern int dpm_noirq_suspend_devices(pm_message_t state);
 extern int dpm_suspend_noirq(pm_message_t state);
 extern int dpm_suspend_late(pm_message_t state);
 extern int dpm_suspend(pm_message_t state);

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 3/4] PM / core: Add error argument to dpm_show_time()
  2017-07-21  0:06 [PATCH 0/4] PM / suspend: Suspend-to-idle core optimization Rafael J. Wysocki
  2017-07-21  0:07 ` [PATCH 1/4] PM / s2idle: Rearrange the main suspend-to-idle loop Rafael J. Wysocki
  2017-07-21  0:09 ` [PATCH 2/4] PM / core: Split dpm_suspend_noirq() and dpm_resume_noirq() Rafael J. Wysocki
@ 2017-07-21  0:10 ` Rafael J. Wysocki
  2017-07-21  0:12 ` [PATCH 4/4] PM / sleep: Check pm_wakeup_pending() in __device_suspend_noirq() Rafael J. Wysocki
  3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2017-07-21  0:10 UTC (permalink / raw)
  To: Linux PM; +Cc: LKML

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Make the core device suspend/resume code also call dpm_show_time()
on failures and add an error argument to this function so that the
message printed by it can reflect the success or failure condition.

This makes the debug messages in question look less confusing in
the failing cases.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/base/power/main.c |   21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

Index: linux-pm/drivers/base/power/main.c
===================================================================
--- linux-pm.orig/drivers/base/power/main.c
+++ linux-pm/drivers/base/power/main.c
@@ -418,7 +418,7 @@ static void pm_dev_err(struct device *de
 		dev_name(dev), pm_verb(state.event), info, error);
 }
 
-static void dpm_show_time(ktime_t starttime, pm_message_t state,
+static void dpm_show_time(ktime_t starttime, pm_message_t state, int error,
 			  const char *info)
 {
 	ktime_t calltime;
@@ -432,8 +432,9 @@ static void dpm_show_time(ktime_t startt
 	if (usecs == 0)
 		usecs = 1;
 
-	pm_pr_dbg("%s%s%s of devices complete after %ld.%03ld msecs\n",
+	pm_pr_dbg("%s%s%s of devices %s after %ld.%03ld msecs\n",
 		  info ?: "", info ? " " : "", pm_verb(state.event),
+		  error ? "aborted" : "complete",
 		  usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
 }
 
@@ -643,7 +644,7 @@ void dpm_noirq_resume_devices(pm_message
 	}
 	mutex_unlock(&dpm_list_mtx);
 	async_synchronize_full();
-	dpm_show_time(starttime, state, "noirq");
+	dpm_show_time(starttime, state, 0, "noirq");
 	trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
 }
 
@@ -782,7 +783,7 @@ void dpm_resume_early(pm_message_t state
 	}
 	mutex_unlock(&dpm_list_mtx);
 	async_synchronize_full();
-	dpm_show_time(starttime, state, "early");
+	dpm_show_time(starttime, state, 0, "early");
 	trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
 }
 
@@ -954,7 +955,7 @@ void dpm_resume(pm_message_t state)
 	}
 	mutex_unlock(&dpm_list_mtx);
 	async_synchronize_full();
-	dpm_show_time(starttime, state, NULL);
+	dpm_show_time(starttime, state, 0, NULL);
 
 	cpufreq_resume();
 	trace_suspend_resume(TPS("dpm_resume"), state.event, false);
@@ -1211,9 +1212,8 @@ int dpm_noirq_suspend_devices(pm_message
 	if (error) {
 		suspend_stats.failed_suspend_noirq++;
 		dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
-	} else {
-		dpm_show_time(starttime, state, "noirq");
 	}
+	dpm_show_time(starttime, state, error, "noirq");
 	trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);
 	return error;
 }
@@ -1371,9 +1371,8 @@ int dpm_suspend_late(pm_message_t state)
 		suspend_stats.failed_suspend_late++;
 		dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
 		dpm_resume_early(resume_event(state));
-	} else {
-		dpm_show_time(starttime, state, "late");
 	}
+	dpm_show_time(starttime, state, error, "late");
 	trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false);
 	return error;
 }
@@ -1639,8 +1638,8 @@ int dpm_suspend(pm_message_t state)
 	if (error) {
 		suspend_stats.failed_suspend++;
 		dpm_save_failed_step(SUSPEND_SUSPEND);
-	} else
-		dpm_show_time(starttime, state, NULL);
+	}
+	dpm_show_time(starttime, state, error, NULL);
 	trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
 	return error;
 }

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 4/4] PM / sleep: Check pm_wakeup_pending() in __device_suspend_noirq()
  2017-07-21  0:06 [PATCH 0/4] PM / suspend: Suspend-to-idle core optimization Rafael J. Wysocki
                   ` (2 preceding siblings ...)
  2017-07-21  0:10 ` [PATCH 3/4] PM / core: Add error argument to dpm_show_time() Rafael J. Wysocki
@ 2017-07-21  0:12 ` Rafael J. Wysocki
  3 siblings, 0 replies; 5+ messages in thread
From: Rafael J. Wysocki @ 2017-07-21  0:12 UTC (permalink / raw)
  To: Linux PM; +Cc: LKML

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Restore the pm_wakeup_pending() check in __device_suspend_noirq()
removed by commit eed4d47efe95 (ACPI / sleep: Ignore spurious SCI
wakeups from suspend-to-idle) as that allows the function to return
earlier if there's a wakeup event pending already (so that it may
spend less time on carrying out operations that will be reversed
shortly anyway) and rework the main suspend-to-idle loop to take
that optimization into account.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/base/power/main.c |    5 +++++
 kernel/power/suspend.c    |   19 ++++++++++++++++---
 2 files changed, 21 insertions(+), 3 deletions(-)

Index: linux-pm/kernel/power/suspend.c
===================================================================
--- linux-pm.orig/kernel/power/suspend.c
+++ linux-pm/kernel/power/suspend.c
@@ -106,19 +106,32 @@ static void s2idle_loop(void)
 {
 	pm_pr_dbg("suspend-to-idle\n");
 
-	while (!dpm_suspend_noirq(PMSG_SUSPEND)) {
+	for (;;) {
+		int error;
+
+		dpm_noirq_begin();
+
 		/*
 		 * Suspend-to-idle equals
 		 * frozen processes + suspended devices + idle processors.
 		 * Thus freeze_enter() should be called right after
 		 * all devices have been suspended.
 		 */
-		freeze_enter();
+		error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
+		if (!error)
+			freeze_enter();
+
+		dpm_noirq_resume_devices(PMSG_RESUME);
+		if (error && (error != -EBUSY || !pm_wakeup_pending())) {
+			dpm_noirq_end();
+			break;
+		}
 
 		if (freeze_ops && freeze_ops->wake)
 			freeze_ops->wake();
 
-		dpm_resume_noirq(PMSG_RESUME);
+		dpm_noirq_end();
+
 		if (freeze_ops && freeze_ops->sync)
 			freeze_ops->sync();
 
Index: linux-pm/drivers/base/power/main.c
===================================================================
--- linux-pm.orig/drivers/base/power/main.c
+++ linux-pm/drivers/base/power/main.c
@@ -1105,6 +1105,11 @@ static int __device_suspend_noirq(struct
 	if (async_error)
 		goto Complete;
 
+	if (pm_wakeup_pending()) {
+		async_error = -EBUSY;
+		goto Complete;
+	}
+
 	if (dev->power.syscore || dev->power.direct_complete)
 		goto Complete;
 

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-07-21  0:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-21  0:06 [PATCH 0/4] PM / suspend: Suspend-to-idle core optimization Rafael J. Wysocki
2017-07-21  0:07 ` [PATCH 1/4] PM / s2idle: Rearrange the main suspend-to-idle loop Rafael J. Wysocki
2017-07-21  0:09 ` [PATCH 2/4] PM / core: Split dpm_suspend_noirq() and dpm_resume_noirq() Rafael J. Wysocki
2017-07-21  0:10 ` [PATCH 3/4] PM / core: Add error argument to dpm_show_time() Rafael J. Wysocki
2017-07-21  0:12 ` [PATCH 4/4] PM / sleep: Check pm_wakeup_pending() in __device_suspend_noirq() Rafael J. Wysocki

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).