[PATCH] Fix /sys/device/.../power/state regression

From: Matthew Garrett
Date: Thu Jan 25 2007 - 00:00:46 EST


In 2.6.19, support for splitting driver suspend and resume callbacks
into interrupt and non-interrupt contexts was added. Unfortunately, this
broke /sys/device/.../power/state support for all devices. In the long
run, this should be obsoleted by power management support in the
individual drivers - however, in the case of network drivers (for
example), currently only three drivers implement any sort of useful
run-time power management.

This patch allows the bus driver to check whether a specific driver
requires the split. If not, the 2.6.18 functionality is restored. It
also alters feature-removals.txt to note that the deprecated
functionality should not be removed until a replacement actually exists.

Signed-off-by: Matthew Garrett <mjg59@xxxxxxxxxxxxx>

--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -9,7 +9,8 @@ be removed from this file.
What: /sys/devices/.../power/state
dev->power.power_state
dpm_runtime_{suspend,resume)()
-When: July 2007
+ bus->pm_has_noirq_stage()
+When: Once alternative functionality has been implemented
Why: Broken design for runtime control over driver power states, confusing
driver-internal runtime power management with: mechanisms to support
system-wide sleep state transitions; event codes that distinguish
diff --git a/Documentation/power/devices.txt b/Documentation/power/devices.txt
index d0e79d5..345cca4 100644
--- a/Documentation/power/devices.txt
+++ b/Documentation/power/devices.txt
@@ -79,6 +79,7 @@ struct bus_type {

int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
+ int (*pm_has_noirq_stage)(struct device *dev);
};

Bus drivers implement those methods as appropriate for the hardware and
@@ -236,6 +237,10 @@ The phases are seen by driver notifications issued in this order:
may stay partly usable until this late. This "late" call may also
help when coping with hardware that behaves badly.

+ If a bus implements the suspend_late method, it must also provide a
+ pm_has_noirq_stage function in order to determine whether devices
+ may be suspended during runtime.
+
The pm_message_t parameter is currently used to refine those semantics
(described later).

@@ -348,7 +353,9 @@ The phases are seen by driver notifications issued in this order:
won't be supported on busses that require IRQs in order to
interact with devices.

- This reverses the effects of bus.suspend_late().
+ This reverses the effects of bus.suspend_late(). As with suspend_late,
+ if a bus implements this function it must provide a pm_has_noirq_stage
+ function.

2 bus.resume(dev) is called next. This may be morphed into a device
driver call with bus-specific parameters; implementations may sleep.
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index f9c903b..6bf1218 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -597,6 +597,16 @@ static int platform_resume(struct device * dev)
return ret;
}

+static int platform_pm_has_noirq_stage(struct device * dev)
+{
+ int ret = 0;
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+
+ if (dev->driver && (drv->resume_early || drv->suspend_late))
+ ret = 1;
+ return ret;
+}
+
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
@@ -606,6 +616,7 @@ struct bus_type platform_bus_type = {
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
+ .pm_has_noirq_stage = platform_pm_has_noirq_stage,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 2d47517..c4ce060 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -46,7 +46,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
int error = -EINVAL;

/* disallow incomplete suspend sequences */
- if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early))
+ if (dev->bus && dev->bus->pm_has_noirq_stage
+ && dev->bus->pm_has_noirq_stage(dev))
return error;

state.event = PM_EVENT_SUSPEND;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index e5ae3a0..c0e4e7a 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -351,6 +351,17 @@ static int pci_device_resume(struct device * dev)
return error;
}

+static int pci_device_pm_has_noirq_stage(struct device * dev)
+{
+ int error = 0;
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
+
+ if (drv && (drv->resume_early || drv->suspend_late))
+ error = 1;
+ return error;
+}
+
static int pci_device_resume_early(struct device * dev)
{
int error = 0;
@@ -569,6 +580,7 @@ struct bus_type pci_bus_type = {
.suspend_late = pci_device_suspend_late,
.resume_early = pci_device_resume_early,
.resume = pci_device_resume,
+ .pm_has_noirq_stage = pci_device_pm_has_noirq_stage,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
};
diff --git a/include/linux/device.h b/include/linux/device.h
index 49ab53c..1c663c4 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -59,6 +59,7 @@ struct bus_type {
int (*suspend)(struct device * dev, pm_message_t state);
int (*suspend_late)(struct device * dev, pm_message_t state);
int (*resume_early)(struct device * dev);
+ int (*pm_has_noirq_stage)(struct device * dev);
int (*resume)(struct device * dev);
};

--
Matthew Garrett | mjg59@xxxxxxxxxxxxx
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/