Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1693671 > unrolled thread

[PATCH 0/3] PCI / ACPI / PM: Fix propagation of wakeup settings to bridges

Started by"Rafael J. Wysocki" <rjw@rjwysocki.net>
First post2017-07-21 15:00 +0200
Last post2017-07-28 02:50 +0200
Articles 11 — 3 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH 0/3] PCI / ACPI / PM: Fix propagation of wakeup settings to bridges "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-21 15:00 +0200
    [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup() "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-21 15:00 +0200
      Re: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup() Andy Shevchenko <andy.shevchenko@gmail.com> - 2017-07-21 17:50 +0200
        Re: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup() "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-21 23:00 +0200
          Re: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup() "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-21 23:20 +0200
      [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup() "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-21 23:40 +0200
        Re: [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup() Andy Shevchenko <andy.shevchenko@gmail.com> - 2017-07-21 23:50 +0200
        Re: [PATCH v2 3/3] ACPI / PCI / PM: Rework  acpi_pci_propagate_wakeup() Mika Westerberg <mika.westerberg@linux.intel.com> - 2017-07-25 14:50 +0200
    [PATCH 1/3] PCI / PM: Skip bridges in pci_enable_wake() "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-21 15:00 +0200
      Re: [PATCH 1/3] PCI / PM: Skip bridges in pci_enable_wake() Mika Westerberg <mika.westerberg@linux.intel.com> - 2017-07-25 14:50 +0200
    Re: [PATCH 0/3] PCI / ACPI / PM: Fix propagation of wakeup settings to bridges "Rafael J. Wysocki" <rjw@rjwysocki.net> - 2017-07-28 02:50 +0200

#1693671 — [PATCH 0/3] PCI / ACPI / PM: Fix propagation of wakeup settings to bridges

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-21 15:00 +0200
Subject[PATCH 0/3] PCI / ACPI / PM: Fix propagation of wakeup settings to bridges
Message-ID<u5FHc-4X6-3@gated-at.bofh.it>
Hi,

The device wakeup code in pci-acpi.c currently has a (theoretical) problem that
it can disable wakeup for a bridge prematurely in some obscure conditions.

This is described in some more detail in the changelog of patch [3/3] and it
has been there for quite a while, so it's nothing new, but it would be good
to fix it at last and hence this series.

Patches [1/3] and [2/3] are mostly preparatory, although the former may be
regarded as a fix by itself and the latter is a cleanup.

Thanks,
Rafael

[toc] | [next] | [standalone]


#1693675 — [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-21 15:00 +0200
Subject[PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u5FHd-4X6-29@gated-at.bofh.it>
In reply to#1693671
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

The acpi_pci_propagate_wakeup() routine is there to handle cases in
which PCI bridges (or PCIe ports) are expected to signal wakeup
for devices below them, but currently it doesn't do that correctly.

The problem is that acpi_pci_propagate_wakeup() uses
acpi_pm_set_device_wakeup() for bridges and if that routine is
called for multiple times to disable wakeup for the same device,
it will disable it on the first invocation and the next calls
will have no effect (it works analogously when called to enable
wakeup, but that is not a problem).

Now, say acpi_pci_propagate_wakeup() has been called for two
different devices under the same bridge and it has called
acpi_pm_set_device_wakeup() for that bridge each time.  The
bridge is now enabled to generate wakeup signals.  Next,
suppose that one of the devices below it resumes and
acpi_pci_propagate_wakeup() is called to disable wakeup for that
device.  It will then call acpi_pm_set_device_wakeup() for the bridge
and that will effectively disable remote wakeup for all devices under
it even though some of them may still be suspended and remote wakeup
may be expected to work for them.

To address this (arguably theoretical) issue, allow
wakeup.enable_count under struct acpi_device to grow beyond 1 in
certain situations.  In particular, allow that to happen in
acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
for PCI bridges, so that wakeup is actually disabled for the
bridge when all devices under it resume and not when just one
of them does that.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/device_pm.c |   49 +++++++++++++++++++++++++++++------------------
 drivers/pci/pci-acpi.c   |    4 +--
 include/acpi/acpi_bus.h  |   14 +++++++++++--
 3 files changed, 45 insertions(+), 22 deletions(-)

Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -682,19 +682,8 @@ static void acpi_pm_notify_work_func(str
 
 static DEFINE_MUTEX(acpi_wakeup_lock);
 
-/**
- * acpi_device_wakeup_enable - Enable wakeup functionality for device.
- * @adev: ACPI device to enable wakeup functionality for.
- * @target_state: State the system is transitioning into.
- *
- * Enable the GPE associated with @adev so that it can generate wakeup signals
- * for the device in response to external (remote) events and enable wakeup
- * power for it.
- *
- * Callers must ensure that @adev is a valid ACPI device node before executing
- * this function.
- */
-static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
+static int __acpi_device_wakeup_enable(struct acpi_device *adev,
+				       u32 target_state, int max_count)
 {
 	struct acpi_device_wakeup *wakeup = &adev->wakeup;
 	acpi_status status;
@@ -702,8 +691,12 @@ static int acpi_device_wakeup_enable(str
 
 	mutex_lock(&acpi_wakeup_lock);
 
-	if (wakeup->enable_count > 0)
-		goto out;
+	if (wakeup->enable_count > 0) {
+		if (wakeup->enable_count < max_count)
+			goto inc;
+		else
+			goto out;
+	}
 
 	error = acpi_enable_wakeup_device_power(adev, target_state);
 	if (error)
@@ -716,6 +709,7 @@ static int acpi_device_wakeup_enable(str
 		goto out;
 	}
 
+inc:
 	wakeup->enable_count++;
 
 out:
@@ -724,6 +718,23 @@ out:
 }
 
 /**
+ * acpi_device_wakeup_enable - Enable wakeup functionality for device.
+ * @adev: ACPI device to enable wakeup functionality for.
+ * @target_state: State the system is transitioning into.
+ *
+ * Enable the GPE associated with @adev so that it can generate wakeup signals
+ * for the device in response to external (remote) events and enable wakeup
+ * power for it.
+ *
+ * Callers must ensure that @adev is a valid ACPI device node before executing
+ * this function.
+ */
+static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
+{
+	return __acpi_device_wakeup_enable(adev, target_state, 1);
+}
+
+/**
  * acpi_device_wakeup_disable - Disable wakeup functionality for device.
  * @adev: ACPI device to disable wakeup functionality for.
  *
@@ -754,8 +765,9 @@ out:
  * acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
  * @dev: Device to enable/disable to generate wakeup events.
  * @enable: Whether to enable or disable the wakeup functionality.
+ * @max_count: Maximum value of the enable reference counter.
  */
-int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count)
 {
 	struct acpi_device *adev;
 	int error;
@@ -775,13 +787,14 @@ int acpi_pm_set_device_wakeup(struct dev
 		return 0;
 	}
 
-	error = acpi_device_wakeup_enable(adev, acpi_target_system_state());
+	error = __acpi_device_wakeup_enable(adev, acpi_target_system_state(),
+					    max_count);
 	if (!error)
 		dev_dbg(dev, "Wakeup enabled by ACPI\n");
 
 	return error;
 }
-EXPORT_SYMBOL(acpi_pm_set_device_wakeup);
+EXPORT_SYMBOL(__acpi_pm_set_device_wakeup);
 
 /**
  * acpi_dev_pm_low_power - Put ACPI device into a low-power state.
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -605,7 +605,7 @@ acpi_status acpi_add_pm_notifier(struct
 acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
 bool acpi_pm_device_can_wakeup(struct device *dev);
 int acpi_pm_device_sleep_state(struct device *, int *, int);
-int acpi_pm_set_device_wakeup(struct device *dev, bool enable);
+int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count);
 #else
 static inline void acpi_pm_wakeup_event(struct device *dev)
 {
@@ -632,12 +632,22 @@ static inline int acpi_pm_device_sleep_s
 	return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ?
 		m : ACPI_STATE_D0;
 }
-static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+static inline int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count)
 {
 	return -ENODEV;
 }
 #endif
 
+static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+{
+	return __acpi_pm_set_device_wakeup(dev, enable, 1);
+}
+
+static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
+{
+	return __acpi_pm_set_device_wakeup(dev, enable, INT_MAX);
+}
+
 #ifdef CONFIG_ACPI_SLEEP
 u32 acpi_target_system_state(void);
 #else
Index: linux-pm/drivers/pci/pci-acpi.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-acpi.c
+++ linux-pm/drivers/pci/pci-acpi.c
@@ -573,7 +573,7 @@ static int acpi_pci_propagate_wakeup(str
 {
 	while (bus->parent) {
 		if (acpi_pm_device_can_wakeup(&bus->self->dev))
-			return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
+			return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable);
 
 		bus = bus->parent;
 	}
@@ -581,7 +581,7 @@ static int acpi_pci_propagate_wakeup(str
 	/* We have reached the root bus. */
 	if (bus->bridge) {
 		if (acpi_pm_device_can_wakeup(bus->bridge))
-			return acpi_pm_set_device_wakeup(bus->bridge, enable);
+			return acpi_pm_set_bridge_wakeup(bus->bridge, enable);
 	}
 	return 0;
 }

[toc] | [prev] | [next] | [standalone]


#1693806 — Re: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

FromAndy Shevchenko <andy.shevchenko@gmail.com>
Date2017-07-21 17:50 +0200
SubjectRe: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u5IlH-6Ek-13@gated-at.bofh.it>
In reply to#1693675
On Fri, Jul 21, 2017 at 3:42 PM, Rafael J. Wysocki <rjw@rjwysocki.net> wrote:

> The acpi_pci_propagate_wakeup() routine is there to handle cases in
> which PCI bridges (or PCIe ports) are expected to signal wakeup
> for devices below them, but currently it doesn't do that correctly.
>
> The problem is that acpi_pci_propagate_wakeup() uses
> acpi_pm_set_device_wakeup() for bridges and if that routine is
> called for multiple times to disable wakeup for the same device,
> it will disable it on the first invocation and the next calls
> will have no effect (it works analogously when called to enable
> wakeup, but that is not a problem).
>
> Now, say acpi_pci_propagate_wakeup() has been called for two
> different devices under the same bridge and it has called
> acpi_pm_set_device_wakeup() for that bridge each time.  The
> bridge is now enabled to generate wakeup signals.  Next,
> suppose that one of the devices below it resumes and
> acpi_pci_propagate_wakeup() is called to disable wakeup for that
> device.  It will then call acpi_pm_set_device_wakeup() for the bridge
> and that will effectively disable remote wakeup for all devices under
> it even though some of them may still be suspended and remote wakeup
> may be expected to work for them.
>
> To address this (arguably theoretical) issue, allow
> wakeup.enable_count under struct acpi_device to grow beyond 1 in
> certain situations.  In particular, allow that to happen in
> acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
> for PCI bridges, so that wakeup is actually disabled for the
> bridge when all devices under it resume and not when just one
> of them does that.

> -       if (wakeup->enable_count > 0)
> -               goto out;
> +       if (wakeup->enable_count > 0) {
> +               if (wakeup->enable_count < max_count)
> +                       goto inc;
> +               else
> +                       goto out;
> +       }

Wouldn't be simpler

    if (wakeup->enable_count >= max_count)
      goto out;

    if (wakeup->enable_count > 0)
      goto inc;

If max_count can be <= 0,

    if (max_count > 0 && wakeup->enable_count >= max_count)
      goto out;


> +inc:
>         wakeup->enable_count++;
>
>  out:



-- 
With Best Regards,
Andy Shevchenko

[toc] | [prev] | [next] | [standalone]


#1693977 — Re: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-21 23:00 +0200
SubjectRe: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u5NbH-18q-9@gated-at.bofh.it>
In reply to#1693806
On Friday, July 21, 2017 06:45:03 PM Andy Shevchenko wrote:
> On Fri, Jul 21, 2017 at 3:42 PM, Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
> 
> > The acpi_pci_propagate_wakeup() routine is there to handle cases in
> > which PCI bridges (or PCIe ports) are expected to signal wakeup
> > for devices below them, but currently it doesn't do that correctly.
> >
> > The problem is that acpi_pci_propagate_wakeup() uses
> > acpi_pm_set_device_wakeup() for bridges and if that routine is
> > called for multiple times to disable wakeup for the same device,
> > it will disable it on the first invocation and the next calls
> > will have no effect (it works analogously when called to enable
> > wakeup, but that is not a problem).
> >
> > Now, say acpi_pci_propagate_wakeup() has been called for two
> > different devices under the same bridge and it has called
> > acpi_pm_set_device_wakeup() for that bridge each time.  The
> > bridge is now enabled to generate wakeup signals.  Next,
> > suppose that one of the devices below it resumes and
> > acpi_pci_propagate_wakeup() is called to disable wakeup for that
> > device.  It will then call acpi_pm_set_device_wakeup() for the bridge
> > and that will effectively disable remote wakeup for all devices under
> > it even though some of them may still be suspended and remote wakeup
> > may be expected to work for them.
> >
> > To address this (arguably theoretical) issue, allow
> > wakeup.enable_count under struct acpi_device to grow beyond 1 in
> > certain situations.  In particular, allow that to happen in
> > acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
> > for PCI bridges, so that wakeup is actually disabled for the
> > bridge when all devices under it resume and not when just one
> > of them does that.
> 
> > -       if (wakeup->enable_count > 0)
> > -               goto out;
> > +       if (wakeup->enable_count > 0) {
> > +               if (wakeup->enable_count < max_count)
> > +                       goto inc;
> > +               else
> > +                       goto out;
> > +       }
> 
> Wouldn't be simpler

I'm not really sure what you mean.

In general, ->

>     if (wakeup->enable_count >= max_count)
>       goto out;

-> this is unlikely and ->>

>     if (wakeup->enable_count > 0)
>       goto inc;

->> this isn't.

Why would checking an unlikely condition before a likely one covering it
ever be better?

> If max_count can be <= 0,

No, it can't be.

Thanks,
Rafael

[toc] | [prev] | [next] | [standalone]


#1693992 — Re: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-21 23:20 +0200
SubjectRe: [PATCH 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u5Nv4-1tI-9@gated-at.bofh.it>
In reply to#1693977
On Friday, July 21, 2017 10:44:30 PM Rafael J. Wysocki wrote:
> On Friday, July 21, 2017 06:45:03 PM Andy Shevchenko wrote:
> > On Fri, Jul 21, 2017 at 3:42 PM, Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
> > 
> > > The acpi_pci_propagate_wakeup() routine is there to handle cases in
> > > which PCI bridges (or PCIe ports) are expected to signal wakeup
> > > for devices below them, but currently it doesn't do that correctly.
> > >
> > > The problem is that acpi_pci_propagate_wakeup() uses
> > > acpi_pm_set_device_wakeup() for bridges and if that routine is
> > > called for multiple times to disable wakeup for the same device,
> > > it will disable it on the first invocation and the next calls
> > > will have no effect (it works analogously when called to enable
> > > wakeup, but that is not a problem).
> > >
> > > Now, say acpi_pci_propagate_wakeup() has been called for two
> > > different devices under the same bridge and it has called
> > > acpi_pm_set_device_wakeup() for that bridge each time.  The
> > > bridge is now enabled to generate wakeup signals.  Next,
> > > suppose that one of the devices below it resumes and
> > > acpi_pci_propagate_wakeup() is called to disable wakeup for that
> > > device.  It will then call acpi_pm_set_device_wakeup() for the bridge
> > > and that will effectively disable remote wakeup for all devices under
> > > it even though some of them may still be suspended and remote wakeup
> > > may be expected to work for them.
> > >
> > > To address this (arguably theoretical) issue, allow
> > > wakeup.enable_count under struct acpi_device to grow beyond 1 in
> > > certain situations.  In particular, allow that to happen in
> > > acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
> > > for PCI bridges, so that wakeup is actually disabled for the
> > > bridge when all devices under it resume and not when just one
> > > of them does that.
> > 
> > > -       if (wakeup->enable_count > 0)
> > > -               goto out;
> > > +       if (wakeup->enable_count > 0) {
> > > +               if (wakeup->enable_count < max_count)
> > > +                       goto inc;
> > > +               else
> > > +                       goto out;
> > > +       }
> > 
> > Wouldn't be simpler
> 
> I'm not really sure what you mean.
> 
> In general, ->
> 
> >     if (wakeup->enable_count >= max_count)
> >       goto out;
> 
> -> this is unlikely and ->>
> 
> >     if (wakeup->enable_count > 0)
> >       goto inc;
> 
> ->> this isn't.
> 
> Why would checking an unlikely condition before a likely one covering it
> ever be better?

OK, the common case is max_cout == 1 and it that case
enable_count >= max_count is equivalent to enable_count > 0,
so I guess fair enough.

Thanks,
Rafael

[toc] | [prev] | [next] | [standalone]


#1693999 — [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-21 23:40 +0200
Subject[PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u5NOq-1zO-5@gated-at.bofh.it>
In reply to#1693675
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

The acpi_pci_propagate_wakeup() routine is there to handle cases in
which PCI bridges (or PCIe ports) are expected to signal wakeup
for devices below them, but currently it doesn't do that correctly.

The problem is that acpi_pci_propagate_wakeup() uses
acpi_pm_set_device_wakeup() for bridges and if that routine is
called for multiple times to disable wakeup for the same device,
it will disable it on the first invocation and the next calls
will have no effect (it works analogously when called to enable
wakeup, but that is not a problem).

Now, say acpi_pci_propagate_wakeup() has been called for two
different devices under the same bridge and it has called
acpi_pm_set_device_wakeup() for that bridge each time.  The
bridge is now enabled to generate wakeup signals.  Next,
suppose that one of the devices below it resumes and
acpi_pci_propagate_wakeup() is called to disable wakeup for that
device.  It will then call acpi_pm_set_device_wakeup() for the bridge
and that will effectively disable remote wakeup for all devices under
it even though some of them may still be suspended and remote wakeup
may be expected to work for them.

To address this (arguably theoretical) issue, allow
wakeup.enable_count under struct acpi_device to grow beyond 1 in
certain situations.  In particular, allow that to happen in
acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
for PCI bridges, so that wakeup is actually disabled for the
bridge when all devices under it resume and not when just one
of them does that.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---

-> v2: Rearrange checks in acpi_device_wakeup_enable() to reduce indentation
          level and possibly save some unnecessary checks for max_count == 1.

---
 drivers/acpi/device_pm.c |   46 +++++++++++++++++++++++++++++-----------------
 drivers/pci/pci-acpi.c   |    4 ++--
 include/acpi/acpi_bus.h  |   14 ++++++++++++--
 3 files changed, 43 insertions(+), 21 deletions(-)

Index: linux-pm/drivers/acpi/device_pm.c
===================================================================
--- linux-pm.orig/drivers/acpi/device_pm.c
+++ linux-pm/drivers/acpi/device_pm.c
@@ -682,19 +682,8 @@ static void acpi_pm_notify_work_func(str
 
 static DEFINE_MUTEX(acpi_wakeup_lock);
 
-/**
- * acpi_device_wakeup_enable - Enable wakeup functionality for device.
- * @adev: ACPI device to enable wakeup functionality for.
- * @target_state: State the system is transitioning into.
- *
- * Enable the GPE associated with @adev so that it can generate wakeup signals
- * for the device in response to external (remote) events and enable wakeup
- * power for it.
- *
- * Callers must ensure that @adev is a valid ACPI device node before executing
- * this function.
- */
-static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
+static int __acpi_device_wakeup_enable(struct acpi_device *adev,
+				       u32 target_state, int max_count)
 {
 	struct acpi_device_wakeup *wakeup = &adev->wakeup;
 	acpi_status status;
@@ -702,9 +691,12 @@ static int acpi_device_wakeup_enable(str
 
 	mutex_lock(&acpi_wakeup_lock);
 
-	if (wakeup->enable_count > 0)
+	if (wakeup->enable_count >= max_count)
 		goto out;
 
+	if (wakeup->enable_count > 0)
+		goto inc;
+
 	error = acpi_enable_wakeup_device_power(adev, target_state);
 	if (error)
 		goto out;
@@ -716,6 +708,7 @@ static int acpi_device_wakeup_enable(str
 		goto out;
 	}
 
+inc:
 	wakeup->enable_count++;
 
 out:
@@ -724,6 +717,23 @@ out:
 }
 
 /**
+ * acpi_device_wakeup_enable - Enable wakeup functionality for device.
+ * @adev: ACPI device to enable wakeup functionality for.
+ * @target_state: State the system is transitioning into.
+ *
+ * Enable the GPE associated with @adev so that it can generate wakeup signals
+ * for the device in response to external (remote) events and enable wakeup
+ * power for it.
+ *
+ * Callers must ensure that @adev is a valid ACPI device node before executing
+ * this function.
+ */
+static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
+{
+	return __acpi_device_wakeup_enable(adev, target_state, 1);
+}
+
+/**
  * acpi_device_wakeup_disable - Disable wakeup functionality for device.
  * @adev: ACPI device to disable wakeup functionality for.
  *
@@ -754,8 +764,9 @@ out:
  * acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
  * @dev: Device to enable/disable to generate wakeup events.
  * @enable: Whether to enable or disable the wakeup functionality.
+ * @max_count: Maximum value of the enable reference counter.
  */
-int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count)
 {
 	struct acpi_device *adev;
 	int error;
@@ -775,13 +786,14 @@ int acpi_pm_set_device_wakeup(struct dev
 		return 0;
 	}
 
-	error = acpi_device_wakeup_enable(adev, acpi_target_system_state());
+	error = __acpi_device_wakeup_enable(adev, acpi_target_system_state(),
+					    max_count);
 	if (!error)
 		dev_dbg(dev, "Wakeup enabled by ACPI\n");
 
 	return error;
 }
-EXPORT_SYMBOL(acpi_pm_set_device_wakeup);
+EXPORT_SYMBOL(__acpi_pm_set_device_wakeup);
 
 /**
  * acpi_dev_pm_low_power - Put ACPI device into a low-power state.
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -605,7 +605,7 @@ acpi_status acpi_add_pm_notifier(struct
 acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
 bool acpi_pm_device_can_wakeup(struct device *dev);
 int acpi_pm_device_sleep_state(struct device *, int *, int);
-int acpi_pm_set_device_wakeup(struct device *dev, bool enable);
+int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count);
 #else
 static inline void acpi_pm_wakeup_event(struct device *dev)
 {
@@ -632,12 +632,22 @@ static inline int acpi_pm_device_sleep_s
 	return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ?
 		m : ACPI_STATE_D0;
 }
-static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+static inline int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count)
 {
 	return -ENODEV;
 }
 #endif
 
+static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
+{
+	return __acpi_pm_set_device_wakeup(dev, enable, 1);
+}
+
+static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
+{
+	return __acpi_pm_set_device_wakeup(dev, enable, INT_MAX);
+}
+
 #ifdef CONFIG_ACPI_SLEEP
 u32 acpi_target_system_state(void);
 #else
Index: linux-pm/drivers/pci/pci-acpi.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-acpi.c
+++ linux-pm/drivers/pci/pci-acpi.c
@@ -573,7 +573,7 @@ static int acpi_pci_propagate_wakeup(str
 {
 	while (bus->parent) {
 		if (acpi_pm_device_can_wakeup(&bus->self->dev))
-			return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
+			return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable);
 
 		bus = bus->parent;
 	}
@@ -581,7 +581,7 @@ static int acpi_pci_propagate_wakeup(str
 	/* We have reached the root bus. */
 	if (bus->bridge) {
 		if (acpi_pm_device_can_wakeup(bus->bridge))
-			return acpi_pm_set_device_wakeup(bus->bridge, enable);
+			return acpi_pm_set_bridge_wakeup(bus->bridge, enable);
 	}
 	return 0;
 }

[toc] | [prev] | [next] | [standalone]


#1694003 — Re: [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

FromAndy Shevchenko <andy.shevchenko@gmail.com>
Date2017-07-21 23:50 +0200
SubjectRe: [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u5NY5-1DW-9@gated-at.bofh.it>
In reply to#1693999
On Sat, Jul 22, 2017 at 12:30 AM, Rafael J. Wysocki <rjw@rjwysocki.net> wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> The acpi_pci_propagate_wakeup() routine is there to handle cases in
> which PCI bridges (or PCIe ports) are expected to signal wakeup
> for devices below them, but currently it doesn't do that correctly.
>
> The problem is that acpi_pci_propagate_wakeup() uses
> acpi_pm_set_device_wakeup() for bridges and if that routine is
> called for multiple times to disable wakeup for the same device,
> it will disable it on the first invocation and the next calls
> will have no effect (it works analogously when called to enable
> wakeup, but that is not a problem).
>
> Now, say acpi_pci_propagate_wakeup() has been called for two
> different devices under the same bridge and it has called
> acpi_pm_set_device_wakeup() for that bridge each time.  The
> bridge is now enabled to generate wakeup signals.  Next,
> suppose that one of the devices below it resumes and
> acpi_pci_propagate_wakeup() is called to disable wakeup for that
> device.  It will then call acpi_pm_set_device_wakeup() for the bridge
> and that will effectively disable remote wakeup for all devices under
> it even though some of them may still be suspended and remote wakeup
> may be expected to work for them.
>
> To address this (arguably theoretical) issue, allow
> wakeup.enable_count under struct acpi_device to grow beyond 1 in
> certain situations.  In particular, allow that to happen in
> acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
> for PCI bridges, so that wakeup is actually disabled for the
> bridge when all devices under it resume and not when just one
> of them does that.

Thanks  for an update! At least to me it's now looks better.

Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>

>
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
>
> -> v2: Rearrange checks in acpi_device_wakeup_enable() to reduce indentation
>           level and possibly save some unnecessary checks for max_count == 1.
>
> ---
>  drivers/acpi/device_pm.c |   46 +++++++++++++++++++++++++++++-----------------
>  drivers/pci/pci-acpi.c   |    4 ++--
>  include/acpi/acpi_bus.h  |   14 ++++++++++++--
>  3 files changed, 43 insertions(+), 21 deletions(-)
>
> Index: linux-pm/drivers/acpi/device_pm.c
> ===================================================================
> --- linux-pm.orig/drivers/acpi/device_pm.c
> +++ linux-pm/drivers/acpi/device_pm.c
> @@ -682,19 +682,8 @@ static void acpi_pm_notify_work_func(str
>
>  static DEFINE_MUTEX(acpi_wakeup_lock);
>
> -/**
> - * acpi_device_wakeup_enable - Enable wakeup functionality for device.
> - * @adev: ACPI device to enable wakeup functionality for.
> - * @target_state: State the system is transitioning into.
> - *
> - * Enable the GPE associated with @adev so that it can generate wakeup signals
> - * for the device in response to external (remote) events and enable wakeup
> - * power for it.
> - *
> - * Callers must ensure that @adev is a valid ACPI device node before executing
> - * this function.
> - */
> -static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
> +static int __acpi_device_wakeup_enable(struct acpi_device *adev,
> +                                      u32 target_state, int max_count)
>  {
>         struct acpi_device_wakeup *wakeup = &adev->wakeup;
>         acpi_status status;
> @@ -702,9 +691,12 @@ static int acpi_device_wakeup_enable(str
>
>         mutex_lock(&acpi_wakeup_lock);
>
> -       if (wakeup->enable_count > 0)
> +       if (wakeup->enable_count >= max_count)
>                 goto out;
>
> +       if (wakeup->enable_count > 0)
> +               goto inc;
> +
>         error = acpi_enable_wakeup_device_power(adev, target_state);
>         if (error)
>                 goto out;
> @@ -716,6 +708,7 @@ static int acpi_device_wakeup_enable(str
>                 goto out;
>         }
>
> +inc:
>         wakeup->enable_count++;
>
>  out:
> @@ -724,6 +717,23 @@ out:
>  }
>
>  /**
> + * acpi_device_wakeup_enable - Enable wakeup functionality for device.
> + * @adev: ACPI device to enable wakeup functionality for.
> + * @target_state: State the system is transitioning into.
> + *
> + * Enable the GPE associated with @adev so that it can generate wakeup signals
> + * for the device in response to external (remote) events and enable wakeup
> + * power for it.
> + *
> + * Callers must ensure that @adev is a valid ACPI device node before executing
> + * this function.
> + */
> +static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
> +{
> +       return __acpi_device_wakeup_enable(adev, target_state, 1);
> +}
> +
> +/**
>   * acpi_device_wakeup_disable - Disable wakeup functionality for device.
>   * @adev: ACPI device to disable wakeup functionality for.
>   *
> @@ -754,8 +764,9 @@ out:
>   * acpi_pm_set_device_wakeup - Enable/disable remote wakeup for given device.
>   * @dev: Device to enable/disable to generate wakeup events.
>   * @enable: Whether to enable or disable the wakeup functionality.
> + * @max_count: Maximum value of the enable reference counter.
>   */
> -int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
> +int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count)
>  {
>         struct acpi_device *adev;
>         int error;
> @@ -775,13 +786,14 @@ int acpi_pm_set_device_wakeup(struct dev
>                 return 0;
>         }
>
> -       error = acpi_device_wakeup_enable(adev, acpi_target_system_state());
> +       error = __acpi_device_wakeup_enable(adev, acpi_target_system_state(),
> +                                           max_count);
>         if (!error)
>                 dev_dbg(dev, "Wakeup enabled by ACPI\n");
>
>         return error;
>  }
> -EXPORT_SYMBOL(acpi_pm_set_device_wakeup);
> +EXPORT_SYMBOL(__acpi_pm_set_device_wakeup);
>
>  /**
>   * acpi_dev_pm_low_power - Put ACPI device into a low-power state.
> Index: linux-pm/include/acpi/acpi_bus.h
> ===================================================================
> --- linux-pm.orig/include/acpi/acpi_bus.h
> +++ linux-pm/include/acpi/acpi_bus.h
> @@ -605,7 +605,7 @@ acpi_status acpi_add_pm_notifier(struct
>  acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
>  bool acpi_pm_device_can_wakeup(struct device *dev);
>  int acpi_pm_device_sleep_state(struct device *, int *, int);
> -int acpi_pm_set_device_wakeup(struct device *dev, bool enable);
> +int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count);
>  #else
>  static inline void acpi_pm_wakeup_event(struct device *dev)
>  {
> @@ -632,12 +632,22 @@ static inline int acpi_pm_device_sleep_s
>         return (m >= ACPI_STATE_D0 && m <= ACPI_STATE_D3_COLD) ?
>                 m : ACPI_STATE_D0;
>  }
> -static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
> +static inline int __acpi_pm_set_device_wakeup(struct device *dev, bool enable, int max_count)
>  {
>         return -ENODEV;
>  }
>  #endif
>
> +static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
> +{
> +       return __acpi_pm_set_device_wakeup(dev, enable, 1);
> +}
> +
> +static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
> +{
> +       return __acpi_pm_set_device_wakeup(dev, enable, INT_MAX);
> +}
> +
>  #ifdef CONFIG_ACPI_SLEEP
>  u32 acpi_target_system_state(void);
>  #else
> Index: linux-pm/drivers/pci/pci-acpi.c
> ===================================================================
> --- linux-pm.orig/drivers/pci/pci-acpi.c
> +++ linux-pm/drivers/pci/pci-acpi.c
> @@ -573,7 +573,7 @@ static int acpi_pci_propagate_wakeup(str
>  {
>         while (bus->parent) {
>                 if (acpi_pm_device_can_wakeup(&bus->self->dev))
> -                       return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
> +                       return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable);
>
>                 bus = bus->parent;
>         }
> @@ -581,7 +581,7 @@ static int acpi_pci_propagate_wakeup(str
>         /* We have reached the root bus. */
>         if (bus->bridge) {
>                 if (acpi_pm_device_can_wakeup(bus->bridge))
> -                       return acpi_pm_set_device_wakeup(bus->bridge, enable);
> +                       return acpi_pm_set_bridge_wakeup(bus->bridge, enable);
>         }
>         return 0;
>  }
>



-- 
With Best Regards,
Andy Shevchenko

[toc] | [prev] | [next] | [standalone]


#1695700 — Re: [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()

FromMika Westerberg <mika.westerberg@linux.intel.com>
Date2017-07-25 14:50 +0200
SubjectRe: [PATCH v2 3/3] ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()
Message-ID<u77rI-2RA-13@gated-at.bofh.it>
In reply to#1693999
On Fri, Jul 21, 2017 at 11:30:24PM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> The acpi_pci_propagate_wakeup() routine is there to handle cases in
> which PCI bridges (or PCIe ports) are expected to signal wakeup
> for devices below them, but currently it doesn't do that correctly.
> 
> The problem is that acpi_pci_propagate_wakeup() uses
> acpi_pm_set_device_wakeup() for bridges and if that routine is
> called for multiple times to disable wakeup for the same device,
> it will disable it on the first invocation and the next calls
> will have no effect (it works analogously when called to enable
> wakeup, but that is not a problem).
> 
> Now, say acpi_pci_propagate_wakeup() has been called for two
> different devices under the same bridge and it has called
> acpi_pm_set_device_wakeup() for that bridge each time.  The
> bridge is now enabled to generate wakeup signals.  Next,
> suppose that one of the devices below it resumes and
> acpi_pci_propagate_wakeup() is called to disable wakeup for that
> device.  It will then call acpi_pm_set_device_wakeup() for the bridge
> and that will effectively disable remote wakeup for all devices under
> it even though some of them may still be suspended and remote wakeup
> may be expected to work for them.
> 
> To address this (arguably theoretical) issue, allow
> wakeup.enable_count under struct acpi_device to grow beyond 1 in
> certain situations.  In particular, allow that to happen in
> acpi_pci_propagate_wakeup() when wakeup is enabled or disabled
> for PCI bridges, so that wakeup is actually disabled for the
> bridge when all devices under it resume and not when just one
> of them does that.
> 
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>

[toc] | [prev] | [next] | [standalone]


#1693677 — [PATCH 1/3] PCI / PM: Skip bridges in pci_enable_wake()

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-21 15:00 +0200
Subject[PATCH 1/3] PCI / PM: Skip bridges in pci_enable_wake()
Message-ID<u5FHd-4X6-33@gated-at.bofh.it>
In reply to#1693671
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

PCI bridges only have a reason to generate wakeup signals on behalf
of devices below them, so avoid preparing bridges for wakeup directly
in pci_enable_wake().

Also drop the pci_has_subordinate() check from pci_pm_default_resume()
as this will be done by pci_enable_wake() itself now.

Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/pci/pci-driver.c |    4 +---
 drivers/pci/pci.c        |    7 +++++++
 2 files changed, 8 insertions(+), 3 deletions(-)

Index: linux-pm/drivers/pci/pci.c
===================================================================
--- linux-pm.orig/drivers/pci/pci.c
+++ linux-pm/drivers/pci/pci.c
@@ -1909,6 +1909,13 @@ int pci_enable_wake(struct pci_dev *dev,
 {
 	int ret = 0;
 
+	/*
+	 * Bridges can only signal wakeup on behalf of subordinate devices,
+	 * but that is set up elsewhere, so skip them.
+	 */
+	if (pci_has_subordinate(dev))
+		return 0;
+
 	/* Don't do the same thing twice in a row for one device. */
 	if (!!enable == !!dev->wakeup_prepared)
 		return 0;
Index: linux-pm/drivers/pci/pci-driver.c
===================================================================
--- linux-pm.orig/drivers/pci/pci-driver.c
+++ linux-pm/drivers/pci/pci-driver.c
@@ -642,9 +642,7 @@ static int pci_legacy_resume(struct devi
 static void pci_pm_default_resume(struct pci_dev *pci_dev)
 {
 	pci_fixup_device(pci_fixup_resume, pci_dev);
-
-	if (!pci_has_subordinate(pci_dev))
-		pci_enable_wake(pci_dev, PCI_D0, false);
+	pci_enable_wake(pci_dev, PCI_D0, false);
 }
 
 static void pci_pm_default_suspend(struct pci_dev *pci_dev)

[toc] | [prev] | [next] | [standalone]


#1695697 — Re: [PATCH 1/3] PCI / PM: Skip bridges in pci_enable_wake()

FromMika Westerberg <mika.westerberg@linux.intel.com>
Date2017-07-25 14:50 +0200
SubjectRe: [PATCH 1/3] PCI / PM: Skip bridges in pci_enable_wake()
Message-ID<u77rH-2RA-5@gated-at.bofh.it>
In reply to#1693677
On Fri, Jul 21, 2017 at 02:38:08PM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> 
> PCI bridges only have a reason to generate wakeup signals on behalf
> of devices below them, so avoid preparing bridges for wakeup directly
> in pci_enable_wake().
> 
> Also drop the pci_has_subordinate() check from pci_pm_default_resume()
> as this will be done by pci_enable_wake() itself now.
> 
> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>

[toc] | [prev] | [next] | [standalone]


#1698388

From"Rafael J. Wysocki" <rjw@rjwysocki.net>
Date2017-07-28 02:50 +0200
Message-ID<u81Dz-4VV-3@gated-at.bofh.it>
In reply to#1693671
On Friday, July 21, 2017 02:36:44 PM Rafael J. Wysocki wrote:
> Hi,
> 
> The device wakeup code in pci-acpi.c currently has a (theoretical) problem that
> it can disable wakeup for a bridge prematurely in some obscure conditions.
> 
> This is described in some more detail in the changelog of patch [3/3] and it
> has been there for quite a while, so it's nothing new, but it would be good
> to fix it at last and hence this series.
> 
> Patches [1/3] and [2/3] are mostly preparatory, although the former may be
> regarded as a fix by itself and the latter is a cleanup.

Hi Bjorn,

Since all of the patches in this series have been reviewed by Mika now, please
let me know if you have any concerns regarding them from the PCI angle.

Otherwise I'm going to queue them up for 4.14 early next week.

Thanks,
Rafael

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web