PM: ACPI: PCI: Drop acpi_pm_set_bridge_wakeup()
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 24 Nov 2020 19:44:00 +0000 (20:44 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 29 Dec 2020 12:47:04 +0000 (13:47 +0100)
commit 7482c5cb90e5a7f9e9e12dd154d405e0219656e3 upstream.

The idea behind acpi_pm_set_bridge_wakeup() was to allow bridges to
be reference counted for wakeup enabling, because they may be enabled
to signal wakeup on behalf of their subordinate devices and that
may happen for multiple times in a row, whereas for the other devices
it only makes sense to enable wakeup signaling once.

However, this becomes problematic if the bridge itself is suspended,
because it is treated as a "regular" device in that case and the
reference counting doesn't work.

For instance, suppose that there are two devices below a bridge and
they both can signal wakeup.  Every time one of them is suspended,
wakeup signaling is enabled for the bridge, so when they both have
been suspended, the bridge's wakeup reference counter value is 2.

Say that the bridge is suspended subsequently and acpi_pci_wakeup()
is called for it.  Because the bridge can signal wakeup, that
function will invoke acpi_pm_set_device_wakeup() to configure it
and __acpi_pm_set_device_wakeup() will be called with the last
argument equal to 1.  This causes __acpi_device_wakeup_enable()
invoked by it to omit the reference counting, because the reference
counter of the target device (the bridge) is 2 at that time.

Now say that the bridge resumes and one of the device below it
resumes too, so the bridge's reference counter becomes 0 and
wakeup signaling is disabled for it, but there is still the other
suspended device which may need the bridge to signal wakeup on its
behalf and that is not going to work.

To address this scenario, use wakeup enable reference counting for
all devices, not just for bridges, so drop the last argument from
__acpi_device_wakeup_enable() and __acpi_pm_set_device_wakeup(),
which causes acpi_pm_set_device_wakeup() and
acpi_pm_set_bridge_wakeup() to become identical, so drop the latter
and use the former instead of it everywhere.

Fixes: 1ba51a7c1496 ("ACPI / PCI / PM: Rework acpi_pci_propagate_wakeup()")
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: 4.14+ <stable@vger.kernel.org> # 4.14+
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/acpi/device_pm.c
drivers/pci/pci-acpi.c
include/acpi/acpi_bus.h

index 6681174caf849c5ebd7f1592becc49f8d85bcd00..0b58ef76da12ea6ae592465e2322161a4cb27edb 100644 (file)
@@ -694,7 +694,7 @@ static void acpi_pm_notify_work_func(struct acpi_device_wakeup_context *context)
 static DEFINE_MUTEX(acpi_wakeup_lock);
 
 static int __acpi_device_wakeup_enable(struct acpi_device *adev,
-                                      u32 target_state, int max_count)
+                                      u32 target_state)
 {
        struct acpi_device_wakeup *wakeup = &adev->wakeup;
        acpi_status status;
@@ -702,9 +702,10 @@ static int __acpi_device_wakeup_enable(struct acpi_device *adev,
 
        mutex_lock(&acpi_wakeup_lock);
 
-       if (wakeup->enable_count >= max_count)
+       if (wakeup->enable_count >= INT_MAX) {
+               acpi_handle_info(adev->handle, "Wakeup enable count out of bounds!\n");
                goto out;
-
+       }
        if (wakeup->enable_count > 0)
                goto inc;
 
@@ -741,7 +742,7 @@ out:
  */
 static int acpi_device_wakeup_enable(struct acpi_device *adev, u32 target_state)
 {
-       return __acpi_device_wakeup_enable(adev, target_state, 1);
+       return __acpi_device_wakeup_enable(adev, target_state);
 }
 
 /**
@@ -771,8 +772,12 @@ out:
        mutex_unlock(&acpi_wakeup_lock);
 }
 
-static int __acpi_pm_set_device_wakeup(struct device *dev, bool enable,
-                                      int max_count)
+/**
+ * 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.
+ */
+int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
 {
        struct acpi_device *adev;
        int error;
@@ -792,36 +797,14 @@ static int __acpi_pm_set_device_wakeup(struct device *dev, bool enable,
                return 0;
        }
 
-       error = __acpi_device_wakeup_enable(adev, acpi_target_system_state(),
-                                           max_count);
+       error = __acpi_device_wakeup_enable(adev, acpi_target_system_state());
        if (!error)
                dev_dbg(dev, "Wakeup enabled by ACPI\n");
 
        return error;
 }
-
-/**
- * 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.
- */
-int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
-{
-       return __acpi_pm_set_device_wakeup(dev, enable, 1);
-}
 EXPORT_SYMBOL_GPL(acpi_pm_set_device_wakeup);
 
-/**
- * acpi_pm_set_bridge_wakeup - Enable/disable remote wakeup for given bridge.
- * @dev: Bridge device to enable/disable to generate wakeup events.
- * @enable: Whether to enable or disable the wakeup functionality.
- */
-int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
-{
-       return __acpi_pm_set_device_wakeup(dev, enable, INT_MAX);
-}
-EXPORT_SYMBOL_GPL(acpi_pm_set_bridge_wakeup);
-
 /**
  * acpi_dev_pm_low_power - Put ACPI device into a low-power state.
  * @dev: Device to put into a low-power state.
index a3cedf8de863005ccfd837e36b731c9e44757e95..fa44e1506357e762cec809b34f00db1fd04e26cf 100644 (file)
@@ -573,7 +573,7 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
 {
        while (bus->parent) {
                if (acpi_pm_device_can_wakeup(&bus->self->dev))
-                       return acpi_pm_set_bridge_wakeup(&bus->self->dev, enable);
+                       return acpi_pm_set_device_wakeup(&bus->self->dev, enable);
 
                bus = bus->parent;
        }
@@ -581,7 +581,7 @@ static int acpi_pci_propagate_wakeup(struct pci_bus *bus, bool enable)
        /* We have reached the root bus. */
        if (bus->bridge) {
                if (acpi_pm_device_can_wakeup(bus->bridge))
-                       return acpi_pm_set_bridge_wakeup(bus->bridge, enable);
+                       return acpi_pm_set_device_wakeup(bus->bridge, enable);
        }
        return 0;
 }
index 324a04df3785b96b409b3a21d886ca6d61c43773..67f4fce222091d3e621dff0bd03e75d543e6b52f 100644 (file)
@@ -619,7 +619,6 @@ 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_bridge_wakeup(struct device *dev, bool enable);
 #else
 static inline void acpi_pm_wakeup_event(struct device *dev)
 {
@@ -650,10 +649,6 @@ static inline int acpi_pm_set_device_wakeup(struct device *dev, bool enable)
 {
        return -ENODEV;
 }
-static inline int acpi_pm_set_bridge_wakeup(struct device *dev, bool enable)
-{
-       return -ENODEV;
-}
 #endif
 
 #ifdef CONFIG_ACPI_SLEEP