linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow
@ 2019-07-16 16:08 Rafael J. Wysocki
  2019-07-16 16:10 ` [PATCH 1/8] PCI: irq: Introduce rearm_wake_irq() Rafael J. Wysocki
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:08 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

Hi All,

The rationale for these changes is explained in the changelog of patch [6/8] as follows:

"After commit 33e4f80ee69b ("ACPI / PM: Ignore spurious SCI wakeups
from suspend-to-idle") the "noirq" phases of device suspend and
resume may run for multiple times during suspend-to-idle, if there
are spurious system wakeup events while suspended.  However, this
is complicated and fragile and actually unnecessary.

The main reason for doing this is that on some systems the EC may
signal system wakeup events (power button events, for example) as
well as events that should not cause the system to resume (spurious
system wakeup events).  Thus, in order to determine whether or not
a given event signaled by the EC while suspended is a proper system
wakeup one, the EC GPE needs to be dispatched and to start with that
was achieved by allowing the ACPI SCI action handler to run, which
was only possible after calling resume_device_irqs().

However, dispatching the EC GPE this way turned out to take too much
time in some cases and some EC events might be missed due to that, so
commit 68e22011856f ("ACPI: EC: Dispatch the EC GPE directly on
s2idle wake") started to dispatch the EC GPE right after a wakeup
event has been detected, so in fact the full ACPI SCI action handler
doesn't need to run any more to deal with the wakeups coming from the
EC.

Use this observation to simplify the suspend-to-idle control flow
so that the "noirq" phases of device suspend and resume are each
run only once in every suspend-to-idle cycle, which is reported to
significantly reduce power drawn by some systems when suspended to
idle (by allowing them to reach a deep platform-wide low-power state
through the suspend-to-idle flow)."

A bonus is that after the essential changes the s2idle flow can be
integrated back into the generic suspend/resume one (patch [7/8])
and some simplifications can be made in drivers/base/power/main.c
after that (patch [8/8]).

Patches [1-5/8] are pre-requisite and the changes made by the first
three of them really take effect after applying patch [6/8].  Patch
[4/8], in turn, is a fix and patch [5/8] is an extra simplification.

Please refer to the changelogs for details.

For easier testing, this series is available from the git branch at:

git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git pm-s2idle-rework

Thanks,
Rafael




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

* [PATCH 1/8] PCI: irq: Introduce rearm_wake_irq()
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
@ 2019-07-16 16:10 ` Rafael J. Wysocki
  2019-07-16 16:11 ` [PATCH 2/8] ACPICA: Return u32 from acpi_dispatch_gpe() Rafael J. Wysocki
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:10 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

Introduce a new function, rearm_wake_irq(), allowing a wakeup IRQ
to be armed for systen wakeup detection again without running any
action handlers associated with it after it has been armed for
wakeup detection and triggered.

That is useful for IRQs, like ACPI SCI, that may deliver wakeup
as well as non-wakeup interrupts when armed for systen wakeup
detection.  In those cases, it may be possible to determine whether
or not the delivered interrupt is a systen wakeup one without
running the entire action handler (or handlers, if the IRQ is
shared) for the IRQ, and if the interrupt turns out to be a
non-wakeup one, the IRQ can be rearmed with the help of the
new function.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 include/linux/interrupt.h |    1 +
 kernel/irq/pm.c           |   20 ++++++++++++++++++++
 2 files changed, 21 insertions(+)

Index: linux-pm/kernel/irq/pm.c
===================================================================
--- linux-pm.orig/kernel/irq/pm.c
+++ linux-pm/kernel/irq/pm.c
@@ -177,6 +177,26 @@ static void resume_irqs(bool want_early)
 }
 
 /**
+ * rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup
+ * @irq: Interrupt to rearm
+ */
+void rearm_wake_irq(unsigned int irq)
+{
+	unsigned long flags;
+	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
+
+	if (!desc || !(desc->istate & IRQS_SUSPENDED) ||
+	    !irqd_is_wakeup_set(&desc->irq_data))
+		return;
+
+	desc->istate &= ~IRQS_SUSPENDED;
+	irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
+	__enable_irq(desc);
+
+	irq_put_desc_busunlock(desc, flags);
+}
+
+/**
  * irq_pm_syscore_ops - enable interrupt lines early
  *
  * Enable all interrupt lines with %IRQF_EARLY_RESUME set.
Index: linux-pm/include/linux/interrupt.h
===================================================================
--- linux-pm.orig/include/linux/interrupt.h
+++ linux-pm/include/linux/interrupt.h
@@ -238,6 +238,7 @@ extern void teardown_percpu_nmi(unsigned
 /* The following three functions are for the core kernel use only. */
 extern void suspend_device_irqs(void);
 extern void resume_device_irqs(void);
+extern void rearm_wake_irq(unsigned int irq);
 
 /**
  * struct irq_affinity_notify - context for notification of IRQ affinity changes




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

* [PATCH 2/8] ACPICA: Return u32 from acpi_dispatch_gpe()
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
  2019-07-16 16:10 ` [PATCH 1/8] PCI: irq: Introduce rearm_wake_irq() Rafael J. Wysocki
@ 2019-07-16 16:11 ` Rafael J. Wysocki
  2019-07-16 16:12 ` [PATCH 3/8] ACPI: EC: Return bool from acpi_ec_dispatch_gpe() Rafael J. Wysocki
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:11 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

In some cases it is useful to know whether or not the
acpi_ev_detect_gpe() called by acpi_dispatch_gpe() has found
the GPE to be active, so return the return value of it (whose
data type is u32) from latter.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/acpica/evxfgpe.c |    6 +++---
 include/acpi/acpixf.h         |    8 +++++++-
 2 files changed, 10 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/acpi/acpica/evxfgpe.c
===================================================================
--- linux-pm.orig/drivers/acpi/acpica/evxfgpe.c
+++ linux-pm/drivers/acpi/acpica/evxfgpe.c
@@ -644,17 +644,17 @@ ACPI_EXPORT_SYMBOL(acpi_get_gpe_status)
  * PARAMETERS:  gpe_device          - Parent GPE Device. NULL for GPE0/GPE1
  *              gpe_number          - GPE level within the GPE block
  *
- * RETURN:      None
+ * RETURN:      INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED
  *
  * DESCRIPTION: Detect and dispatch a General Purpose Event to either a function
  *              (e.g. EC) or method (e.g. _Lxx/_Exx) handler.
  *
  ******************************************************************************/
-void acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)
+u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number)
 {
 	ACPI_FUNCTION_TRACE(acpi_dispatch_gpe);
 
-	acpi_ev_detect_gpe(gpe_device, NULL, gpe_number);
+	return acpi_ev_detect_gpe(gpe_device, NULL, gpe_number);
 }
 
 ACPI_EXPORT_SYMBOL(acpi_dispatch_gpe)
Index: linux-pm/include/acpi/acpixf.h
===================================================================
--- linux-pm.orig/include/acpi/acpixf.h
+++ linux-pm/include/acpi/acpixf.h
@@ -297,6 +297,9 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_an
 #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
 	ACPI_EXTERNAL_RETURN_OK(prototype)
 
+#define ACPI_HW_DEPENDENT_RETURN_UINT32(prototype) \
+	ACPI_EXTERNAL_RETURN_UINT32(prototype)
+
 #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
 	ACPI_EXTERNAL_RETURN_VOID(prototype)
 
@@ -307,6 +310,9 @@ ACPI_GLOBAL(u8, acpi_gbl_system_awake_an
 #define ACPI_HW_DEPENDENT_RETURN_OK(prototype) \
 	static ACPI_INLINE prototype {return(AE_OK);}
 
+#define ACPI_HW_DEPENDENT_RETURN_UINT32(prototype) \
+	static ACPI_INLINE prototype {return(0);}
+
 #define ACPI_HW_DEPENDENT_RETURN_VOID(prototype) \
 	static ACPI_INLINE prototype {return;}
 
@@ -738,7 +744,7 @@ ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_sta
 						     u32 gpe_number,
 						     acpi_event_status
 						     *event_status))
-ACPI_HW_DEPENDENT_RETURN_VOID(void acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number))
+ACPI_HW_DEPENDENT_RETURN_UINT32(u32 acpi_dispatch_gpe(acpi_handle gpe_device, u32 gpe_number))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_disable_all_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_runtime_gpes(void))
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status acpi_enable_all_wakeup_gpes(void))




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

* [PATCH 3/8] ACPI: EC: Return bool from acpi_ec_dispatch_gpe()
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
  2019-07-16 16:10 ` [PATCH 1/8] PCI: irq: Introduce rearm_wake_irq() Rafael J. Wysocki
  2019-07-16 16:11 ` [PATCH 2/8] ACPICA: Return u32 from acpi_dispatch_gpe() Rafael J. Wysocki
@ 2019-07-16 16:12 ` Rafael J. Wysocki
  2019-07-16 16:14 ` [PATCH 4/8] PM: sleep: Fix possible overflow in pm_system_cancel_wakeup() Rafael J. Wysocki
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:12 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

On some systems, if suspend-to-idle is used, the EC may singal system
wakeup events (power button events, for example) as well as events
that should not cause the system to resume and acpi_ec_dispatch_gpe()
needs to be called to determine whether or not the system should
resume then.  In particular, if acpi_ec_dispatch_gpe() doesn't detect
any EC events at all, the system should remain suspended, so it is
useful to know when that is the case.

For this reason, make acpi_ec_dispatch_gpe() return a bool value
indicating whether or not any EC events have been detected by it.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/ec.c       |   11 ++++++++---
 drivers/acpi/internal.h |    2 +-
 2 files changed, 9 insertions(+), 4 deletions(-)

Index: linux-pm/drivers/acpi/ec.c
===================================================================
--- linux-pm.orig/drivers/acpi/ec.c
+++ linux-pm/drivers/acpi/ec.c
@@ -1060,10 +1060,15 @@ void acpi_ec_set_gpe_wake_mask(u8 action
 		acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action);
 }
 
-void acpi_ec_dispatch_gpe(void)
+bool acpi_ec_dispatch_gpe(void)
 {
-	if (first_ec)
-		acpi_dispatch_gpe(NULL, first_ec->gpe);
+	u32 ret;
+
+	if (!first_ec)
+		return false;
+
+	ret = acpi_dispatch_gpe(NULL, first_ec->gpe);
+	return ret == ACPI_INTERRUPT_HANDLED;
 }
 
 /* --------------------------------------------------------------------------
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -196,7 +196,7 @@ void acpi_ec_block_transactions(void);
 void acpi_ec_unblock_transactions(void);
 void acpi_ec_mark_gpe_for_wake(void);
 void acpi_ec_set_gpe_wake_mask(u8 action);
-void acpi_ec_dispatch_gpe(void);
+bool acpi_ec_dispatch_gpe(void);
 int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
 			      acpi_handle handle, acpi_ec_query_func func,
 			      void *data);




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

* [PATCH 4/8] PM: sleep: Fix possible overflow in pm_system_cancel_wakeup()
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
                   ` (2 preceding siblings ...)
  2019-07-16 16:12 ` [PATCH 3/8] ACPI: EC: Return bool from acpi_ec_dispatch_gpe() Rafael J. Wysocki
@ 2019-07-16 16:14 ` Rafael J. Wysocki
  2019-07-16 16:15 ` [PATCH 5/8] ACPI: PM: Set s2idle_wakeup earlier and clear it later Rafael J. Wysocki
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:14 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

It is not actually guaranteed that pm_abort_suspend will be
nonzero when pm_system_cancel_wakeup() is called which may lead to
subtle issues, so make it use atomic_dec_if_positive() instead of
atomic_dec() for the safety sake.

Fixes: 33e4f80ee69b ("ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle")
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/base/power/wakeup.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-pm/drivers/base/power/wakeup.c
===================================================================
--- linux-pm.orig/drivers/base/power/wakeup.c
+++ linux-pm/drivers/base/power/wakeup.c
@@ -859,7 +859,7 @@ EXPORT_SYMBOL_GPL(pm_system_wakeup);
 
 void pm_system_cancel_wakeup(void)
 {
-	atomic_dec(&pm_abort_suspend);
+	atomic_dec_if_positive(&pm_abort_suspend);
 }
 
 void pm_wakeup_clear(bool reset)




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

* [PATCH 5/8] ACPI: PM: Set s2idle_wakeup earlier and clear it later
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
                   ` (3 preceding siblings ...)
  2019-07-16 16:14 ` [PATCH 4/8] PM: sleep: Fix possible overflow in pm_system_cancel_wakeup() Rafael J. Wysocki
@ 2019-07-16 16:15 ` Rafael J. Wysocki
  2019-07-16 16:17 ` [PATCH 6/8] PM: sleep: Simplify suspend-to-idle control flow Rafael J. Wysocki
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:15 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

The role of the s2idle_wakeup variable is to cause
acpi_pm_wakeup_event() and acpi_pm_notify_handler() to
increment pm_abort_suspend and trigger a wakeup from
suspend-to-idle in case the ACPI SCI wakeup was canceled
by acpi_s2idle_wake().

However, for this purpose it need not be set in acpi_s2idle_wake()
and cleared in acpi_s2idle_sync(), respectively.  In fact, it
may be set as early as in acpi_s2idle_prepare() and cleared as
late as in acpi_s2idle_restore(), so do that to allow subsequent
changes to be simpler.

This change is not expected to alter functionality.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/sleep.c |    6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

Index: linux-pm/drivers/acpi/sleep.c
===================================================================
--- linux-pm.orig/drivers/acpi/sleep.c
+++ linux-pm/drivers/acpi/sleep.c
@@ -972,6 +972,8 @@ static int acpi_s2idle_prepare(void)
 	/* Change the configuration of GPEs to avoid spurious wakeup. */
 	acpi_enable_all_wakeup_gpes();
 	acpi_os_wait_events_complete();
+
+	s2idle_wakeup = true;
 	return 0;
 }
 
@@ -991,7 +993,6 @@ static void acpi_s2idle_wake(void)
 	if (acpi_sci_irq_valid() &&
 	    !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
 		pm_system_cancel_wakeup();
-		s2idle_wakeup = true;
 		/*
 		 * On some platforms with the LPS0 _DSM device noirq resume
 		 * takes too much time for EC wakeup events to survive, so look
@@ -1012,11 +1013,12 @@ static void acpi_s2idle_sync(void)
 	acpi_os_wait_events_complete();	/* synchronize SCI IRQ handling */
 	acpi_ec_flush_work();
 	acpi_os_wait_events_complete();	/* synchronize Notify handling */
-	s2idle_wakeup = false;
 }
 
 static void acpi_s2idle_restore(void)
 {
+	s2idle_wakeup = false;
+
 	acpi_enable_all_runtime_gpes();
 
 	acpi_disable_wakeup_devices(ACPI_STATE_S0);




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

* [PATCH 6/8] PM: sleep: Simplify suspend-to-idle control flow
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
                   ` (4 preceding siblings ...)
  2019-07-16 16:15 ` [PATCH 5/8] ACPI: PM: Set s2idle_wakeup earlier and clear it later Rafael J. Wysocki
@ 2019-07-16 16:17 ` Rafael J. Wysocki
  2019-07-16 16:20 ` [PATCH 7/8] PM: sleep: Integrate suspend-to-idle with generig suspend flow Rafael J. Wysocki
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:17 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

After commit 33e4f80ee69b ("ACPI / PM: Ignore spurious SCI wakeups
from suspend-to-idle") the "noirq" phases of device suspend and
resume may run for multiple times during suspend-to-idle, if there
are spurious system wakeup events while suspended.  However, this
is complicated and fragile and actually unnecessary.

The main reason for doing this is that on some systems the EC may
signal system wakeup events (power button events, for example) as
well as events that should not cause the system to resume (spurious
system wakeup events).  Thus, in order to determine whether or not
a given event signaled by the EC while suspended is a proper system
wakeup one, the EC GPE needs to be dispatched and to start with that
was achieved by allowing the ACPI SCI action handler to run, which
was only possible after calling resume_device_irqs().

However, dispatching the EC GPE this way turned out to take too much
time in some cases and some EC events might be missed due to that, so
commit 68e22011856f ("ACPI: EC: Dispatch the EC GPE directly on
s2idle wake") started to dispatch the EC GPE right after a wakeup
event has been detected, so in fact the full ACPI SCI action handler
doesn't need to run any more to deal with the wakeups coming from the
EC.

Use this observation to simplify the suspend-to-idle control flow
so that the "noirq" phases of device suspend and resume are each
run only once in every suspend-to-idle cycle, which is reported to
significantly reduce power drawn by some systems when suspended to
idle (by allowing them to reach a deep platform-wide low-power state
through the suspend-to-idle flow).  [What appears to happen is that
the "noirq" resume of devices after a spurious EC wakeup brings some
devices into a state in which they prevent the platform from reaching
the deep low-power state going forward, even after a subsequent
"noirq" suspend phase, and on some systems the EC triggers such
wakeups already when the "noirq" suspend of devices is running for
the first time in the given suspend/resume cycle, so the platform
cannot reach the deep low-power state at all.]

First, make acpi_s2idle_wake() use the acpi_ec_dispatch_gpe() return
value to determine whether or not the wakeup may have been triggered
by the EC (in which case the system wakeup is canceled and ACPI
events are processed in order to determine whether or not the event
is a proper system wakeup one) and use rearm_wake_irq() (introduced
by a previous change) in it to rearm the ACPI SCI for system wakeup
detection in case the system will remain suspended.

Second, drop acpi_s2idle_sync(), which is not needed any more, and
the corresponding global platform suspend-to-idle callback.

Next, drop the pm_wakeup_pending() check (which is an optimization
only) from __device_suspend_noirq() to prevent it from returning
errors on system wakeups occurring before the "noirq" phase of
device suspend is complete (as in the case of suspend-to-idle it is
not known whether or not these wakeups are suprious at that point),
in order to avoid having to carry out a "noirq" resume of devices
on a spurious system wakeup.

Finally, change the code flow in s2idle_loop() to (1) run the
"noirq" suspend of devices once before starting the loop, (2) check
for spurious EC wakeups (via the platform ->wake callback) for the
first time before calling s2idle_enter(), and (3) run the "noirq"
resume of devices once after leaving the loop.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/sleep.c      |   47 +++++++++++++++++++++-------------------
 drivers/base/power/main.c |    5 ----
 include/linux/suspend.h   |    1 
 kernel/power/suspend.c    |   53 +++++++++++++++++++---------------------------
 4 files changed, 48 insertions(+), 58 deletions(-)

Index: linux-pm/drivers/acpi/sleep.c
===================================================================
--- linux-pm.orig/drivers/acpi/sleep.c
+++ linux-pm/drivers/acpi/sleep.c
@@ -986,33 +986,37 @@ static void acpi_s2idle_wake(void)
 		lpi_check_constraints();
 
 	/*
-	 * If IRQD_WAKEUP_ARMED is not set for the SCI at this point, it means
-	 * that the SCI has triggered while suspended, so cancel the wakeup in
-	 * case it has not been a wakeup event (the GPEs will be checked later).
+	 * If IRQD_WAKEUP_ARMED is set for the SCI at this point, the SCI has
+	 * not triggered while suspended, so bail out.
 	 */
-	if (acpi_sci_irq_valid() &&
-	    !irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq))) {
+	if (!acpi_sci_irq_valid() ||
+	    irqd_is_wakeup_armed(irq_get_irq_data(acpi_sci_irq)))
+		return;
+
+	/*
+	 * If there are EC events to process, the wakeup may be a spurious one
+	 * coming from the EC.
+	 */
+	if (acpi_ec_dispatch_gpe()) {
+		/*
+		 * Cancel the wakeup and process all pending events in case
+		 * there are any wakeup ones in there.
+		 *
+		 * Note that if any non-EC GPEs are active at this point, the
+		 * SCI will retrigger after the rearming below, so no events
+		 * should be missed by canceling the wakeup here.
+		 */
 		pm_system_cancel_wakeup();
 		/*
-		 * On some platforms with the LPS0 _DSM device noirq resume
-		 * takes too much time for EC wakeup events to survive, so look
-		 * for them now.
+		 * The EC driver uses the system workqueue and an additional
+		 * special one, so those need to be flushed too.
 		 */
-		acpi_ec_dispatch_gpe();
+		acpi_os_wait_events_complete(); /* synchronize EC GPE processing */
+		acpi_ec_flush_work();
+		acpi_os_wait_events_complete(); /* synchronize Notify handling */
 	}
-}
 
-static void acpi_s2idle_sync(void)
-{
-	/*
-	 * Process all pending events in case there are any wakeup ones.
-	 *
-	 * The EC driver uses the system workqueue and an additional special
-	 * one, so those need to be flushed too.
-	 */
-	acpi_os_wait_events_complete();	/* synchronize SCI IRQ handling */
-	acpi_ec_flush_work();
-	acpi_os_wait_events_complete();	/* synchronize Notify handling */
+	rearm_wake_irq(acpi_sci_irq);
 }
 
 static void acpi_s2idle_restore(void)
@@ -1044,7 +1048,6 @@ static const struct platform_s2idle_ops
 	.begin = acpi_s2idle_begin,
 	.prepare = acpi_s2idle_prepare,
 	.wake = acpi_s2idle_wake,
-	.sync = acpi_s2idle_sync,
 	.restore = acpi_s2idle_restore,
 	.end = acpi_s2idle_end,
 };
Index: linux-pm/include/linux/suspend.h
===================================================================
--- linux-pm.orig/include/linux/suspend.h
+++ linux-pm/include/linux/suspend.h
@@ -191,7 +191,6 @@ struct platform_s2idle_ops {
 	int (*begin)(void);
 	int (*prepare)(void);
 	void (*wake)(void);
-	void (*sync)(void);
 	void (*restore)(void);
 	void (*end)(void);
 };
Index: linux-pm/drivers/base/power/main.c
===================================================================
--- linux-pm.orig/drivers/base/power/main.c
+++ linux-pm/drivers/base/power/main.c
@@ -1291,11 +1291,6 @@ 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;
 
Index: linux-pm/kernel/power/suspend.c
===================================================================
--- linux-pm.orig/kernel/power/suspend.c
+++ linux-pm/kernel/power/suspend.c
@@ -119,48 +119,41 @@ static void s2idle_enter(void)
 
 static void s2idle_loop(void)
 {
-	pm_pr_dbg("suspend-to-idle\n");
-
-	for (;;) {
-		int error;
+	int error;
 
-		dpm_noirq_begin();
+	dpm_noirq_begin();
+	error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
+	if (error)
+		goto resume;
 
-		/*
-		 * Suspend-to-idle equals
-		 * frozen processes + suspended devices + idle processors.
-		 * Thus s2idle_enter() should be called right after
-		 * all devices have been suspended.
-		 *
-		 * Wakeups during the noirq suspend of devices may be spurious,
-		 * so prevent them from terminating the loop right away.
-		 */
-		error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
-		if (!error)
-			s2idle_enter();
-		else if (error == -EBUSY && pm_wakeup_pending())
-			error = 0;
+	pm_pr_dbg("suspend-to-idle\n");
 
-		if (!error && s2idle_ops && s2idle_ops->wake)
+	/*
+	 * Suspend-to-idle equals:
+	 * frozen processes + suspended devices + idle processors.
+	 * Thus s2idle_enter() should be called right after all devices have
+	 * been suspended.
+	 *
+	 * Wakeups during the noirq suspend of devices may be spurious, so try
+	 * to avoid them upfront.
+	 */
+	for (;;) {
+		if (s2idle_ops && s2idle_ops->wake)
 			s2idle_ops->wake();
 
-		dpm_noirq_resume_devices(PMSG_RESUME);
-
-		dpm_noirq_end();
-
-		if (error)
-			break;
-
-		if (s2idle_ops && s2idle_ops->sync)
-			s2idle_ops->sync();
-
 		if (pm_wakeup_pending())
 			break;
 
 		pm_wakeup_clear(false);
+
+		s2idle_enter();
 	}
 
 	pm_pr_dbg("resume from suspend-to-idle\n");
+
+resume:
+	dpm_noirq_resume_devices(PMSG_RESUME);
+	dpm_noirq_end();
 }
 
 void s2idle_wake(void)




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

* [PATCH 7/8] PM: sleep: Integrate suspend-to-idle with generig suspend flow
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
                   ` (5 preceding siblings ...)
  2019-07-16 16:17 ` [PATCH 6/8] PM: sleep: Simplify suspend-to-idle control flow Rafael J. Wysocki
@ 2019-07-16 16:20 ` Rafael J. Wysocki
  2019-07-16 16:21 ` [PATCH 8/8] PM: sleep: Drop dpm_noirq_begin() and dpm_noirq_end() Rafael J. Wysocki
  2019-07-22  9:46 ` [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Thomas Gleixner
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:20 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

After previous changes the suspend-to-idle code flow can be
integrated more tightly with the generic system suspend code flow
by making suspend_enter() call s2idle_loop() later and removing
the direct invocations of dpm_noirq_begin(),
dpm_noirq_suspend_devices(), dpm_noirq_end(), and
dpm_noirq_resume_devices() from the latter, so do that.

This change is not expected to alter functionality.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 kernel/power/suspend.c |   21 +++++----------------
 1 file changed, 5 insertions(+), 16 deletions(-)

Index: linux-pm/kernel/power/suspend.c
===================================================================
--- linux-pm.orig/kernel/power/suspend.c
+++ linux-pm/kernel/power/suspend.c
@@ -119,13 +119,6 @@ static void s2idle_enter(void)
 
 static void s2idle_loop(void)
 {
-	int error;
-
-	dpm_noirq_begin();
-	error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
-	if (error)
-		goto resume;
-
 	pm_pr_dbg("suspend-to-idle\n");
 
 	/*
@@ -150,10 +143,6 @@ static void s2idle_loop(void)
 	}
 
 	pm_pr_dbg("resume from suspend-to-idle\n");
-
-resume:
-	dpm_noirq_resume_devices(PMSG_RESUME);
-	dpm_noirq_end();
 }
 
 void s2idle_wake(void)
@@ -408,11 +397,6 @@ static int suspend_enter(suspend_state_t
 	if (error)
 		goto Devices_early_resume;
 
-	if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) {
-		s2idle_loop();
-		goto Platform_early_resume;
-	}
-
 	error = dpm_suspend_noirq(PMSG_SUSPEND);
 	if (error) {
 		pr_err("noirq suspend of devices failed\n");
@@ -425,6 +409,11 @@ static int suspend_enter(suspend_state_t
 	if (suspend_test(TEST_PLATFORM))
 		goto Platform_wake;
 
+	if (state == PM_SUSPEND_TO_IDLE) {
+		s2idle_loop();
+		goto Platform_wake;
+	}
+
 	error = suspend_disable_secondary_cpus();
 	if (error || suspend_test(TEST_CPUS))
 		goto Enable_cpus;




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

* [PATCH 8/8] PM: sleep: Drop dpm_noirq_begin() and dpm_noirq_end()
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
                   ` (6 preceding siblings ...)
  2019-07-16 16:20 ` [PATCH 7/8] PM: sleep: Integrate suspend-to-idle with generig suspend flow Rafael J. Wysocki
@ 2019-07-16 16:21 ` Rafael J. Wysocki
  2019-07-22  9:46 ` [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Thomas Gleixner
  8 siblings, 0 replies; 10+ messages in thread
From: Rafael J. Wysocki @ 2019-07-16 16:21 UTC (permalink / raw)
  To: Linux PM
  Cc: Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Thomas Gleixner, Rajneesh Bhardwaj, David E. Box

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

Note that after previous changes dpm_noirq_begin() and
dpm_noirq_end() each have only one caller, so move the code from
them to their respective callers and drop them.

Also note that dpm_noirq_resume_devices() and
dpm_noirq_suspend_devices() need not be exported any more, so make
them both static.

This change is not expected to alter functionality.

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

Index: linux-pm/drivers/base/power/main.c
===================================================================
--- linux-pm.orig/drivers/base/power/main.c
+++ linux-pm/drivers/base/power/main.c
@@ -716,7 +716,7 @@ static void async_resume_noirq(void *dat
 	put_device(dev);
 }
 
-void dpm_noirq_resume_devices(pm_message_t state)
+static void dpm_noirq_resume_devices(pm_message_t state)
 {
 	struct device *dev;
 	ktime_t starttime = ktime_get();
@@ -760,13 +760,6 @@ void dpm_noirq_resume_devices(pm_message
 	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();
-}
-
 /**
  * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
  * @state: PM transition of the system being carried out.
@@ -777,7 +770,11 @@ void dpm_noirq_end(void)
 void dpm_resume_noirq(pm_message_t state)
 {
 	dpm_noirq_resume_devices(state);
-	dpm_noirq_end();
+
+	resume_device_irqs();
+	device_wakeup_disarm_wake_irqs();
+
+	cpuidle_resume();
 }
 
 static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev,
@@ -1357,14 +1354,7 @@ static int device_suspend_noirq(struct d
 	return __device_suspend_noirq(dev, pm_transition, false);
 }
 
-void dpm_noirq_begin(void)
-{
-	cpuidle_pause();
-	device_wakeup_arm_wake_irqs();
-	suspend_device_irqs();
-}
-
-int dpm_noirq_suspend_devices(pm_message_t state)
+static int dpm_noirq_suspend_devices(pm_message_t state)
 {
 	ktime_t starttime = ktime_get();
 	int error = 0;
@@ -1421,7 +1411,11 @@ int dpm_suspend_noirq(pm_message_t state
 {
 	int ret;
 
-	dpm_noirq_begin();
+	cpuidle_pause();
+
+	device_wakeup_arm_wake_irqs();
+	suspend_device_irqs();
+
 	ret = dpm_noirq_suspend_devices(state);
 	if (ret)
 		dpm_resume_noirq(resume_event(state));
Index: linux-pm/include/linux/pm.h
===================================================================
--- linux-pm.orig/include/linux/pm.h
+++ linux-pm/include/linux/pm.h
@@ -712,8 +712,6 @@ 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);
@@ -722,8 +720,6 @@ 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] 10+ messages in thread

* Re: [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow
  2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
                   ` (7 preceding siblings ...)
  2019-07-16 16:21 ` [PATCH 8/8] PM: sleep: Drop dpm_noirq_begin() and dpm_noirq_end() Rafael J. Wysocki
@ 2019-07-22  9:46 ` Thomas Gleixner
  8 siblings, 0 replies; 10+ messages in thread
From: Thomas Gleixner @ 2019-07-22  9:46 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM, Linux ACPI, LKML, Len Brown, Srinivas Pandruvada,
	Rajneesh Bhardwaj, David E. Box

On Tue, 16 Jul 2019, Rafael J. Wysocki wrote:

> Hi All,
> 
> The rationale for these changes is explained in the changelog of patch [6/8] as follows:
> 
> "After commit 33e4f80ee69b ("ACPI / PM: Ignore spurious SCI wakeups
> from suspend-to-idle") the "noirq" phases of device suspend and
> resume may run for multiple times during suspend-to-idle, if there
> are spurious system wakeup events while suspended.  However, this
> is complicated and fragile and actually unnecessary.
> 
> The main reason for doing this is that on some systems the EC may
> signal system wakeup events (power button events, for example) as
> well as events that should not cause the system to resume (spurious
> system wakeup events).  Thus, in order to determine whether or not
> a given event signaled by the EC while suspended is a proper system
> wakeup one, the EC GPE needs to be dispatched and to start with that
> was achieved by allowing the ACPI SCI action handler to run, which
> was only possible after calling resume_device_irqs().
> 
> However, dispatching the EC GPE this way turned out to take too much
> time in some cases and some EC events might be missed due to that, so
> commit 68e22011856f ("ACPI: EC: Dispatch the EC GPE directly on
> s2idle wake") started to dispatch the EC GPE right after a wakeup
> event has been detected, so in fact the full ACPI SCI action handler
> doesn't need to run any more to deal with the wakeups coming from the
> EC.
> 
> Use this observation to simplify the suspend-to-idle control flow
> so that the "noirq" phases of device suspend and resume are each
> run only once in every suspend-to-idle cycle, which is reported to
> significantly reduce power drawn by some systems when suspended to
> idle (by allowing them to reach a deep platform-wide low-power state
> through the suspend-to-idle flow)."
> 
> A bonus is that after the essential changes the s2idle flow can be
> integrated back into the generic suspend/resume one (patch [7/8])
> and some simplifications can be made in drivers/base/power/main.c
> after that (patch [8/8]).

Nice work!

Acked-by: Thomas Gleixner <tglx@linutronix.de>


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

end of thread, other threads:[~2019-07-22  9:46 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-16 16:08 [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Rafael J. Wysocki
2019-07-16 16:10 ` [PATCH 1/8] PCI: irq: Introduce rearm_wake_irq() Rafael J. Wysocki
2019-07-16 16:11 ` [PATCH 2/8] ACPICA: Return u32 from acpi_dispatch_gpe() Rafael J. Wysocki
2019-07-16 16:12 ` [PATCH 3/8] ACPI: EC: Return bool from acpi_ec_dispatch_gpe() Rafael J. Wysocki
2019-07-16 16:14 ` [PATCH 4/8] PM: sleep: Fix possible overflow in pm_system_cancel_wakeup() Rafael J. Wysocki
2019-07-16 16:15 ` [PATCH 5/8] ACPI: PM: Set s2idle_wakeup earlier and clear it later Rafael J. Wysocki
2019-07-16 16:17 ` [PATCH 6/8] PM: sleep: Simplify suspend-to-idle control flow Rafael J. Wysocki
2019-07-16 16:20 ` [PATCH 7/8] PM: sleep: Integrate suspend-to-idle with generig suspend flow Rafael J. Wysocki
2019-07-16 16:21 ` [PATCH 8/8] PM: sleep: Drop dpm_noirq_begin() and dpm_noirq_end() Rafael J. Wysocki
2019-07-22  9:46 ` [PATCH 0/8] PM / ACPI: sleep: Simplify the suspend-to-idle control flow Thomas Gleixner

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).