aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2009-01-26 15:43:08 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-01-27 12:45:46 -0500
commit418e4da33f45fd7bdcce48778b149b780ff730bc (patch)
treefca80fd94c2ec3ce7c7384daa07c9783ed33d96a
parent5ee810072175042775e39bdd3eaaa68884c27805 (diff)
PCI PM: Fix suspend error paths and testing facility breakage
If one of device drivers refuses to suspend by returning error code from its ->suspend() callback, the devices that have already been suspended are resumed by executing their drivers' ->resume() callbacks. Some of these callbacks expect the device's configuration space to be restored if the device has been put into D3 before they are called. Unfortunately, this mechanism has been broken by recent changes moving the restoration of config spaces of some devices (most importantly, USB controllers and HDA Intel) into the resume callbacks executed with interrupts off. Obviously, these callbacks are not invoked in the suspend error path and, as a result, the system cannot be successfully brought back into the working state in case of a suspend error. The same thing happens in the hibernation error path right before putting the system into S4. Similarly, the suspend testing facility associated with the /sys/power/pm_test file is broken, because it uses the very same mechanism that is used in the suspend and hibernation error paths. Fix the breakage by making the PCI core restore the configuration spaces of PCI devices that haven't been restored already before pci_pm_resume() is called for those devices by the PM core. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--drivers/pci/pci-driver.c16
1 files changed, 16 insertions, 0 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 9de07b75b993..4884c4840b3d 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -370,6 +370,7 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state)
370 } 370 }
371 371
372 pci_save_state(pci_dev); 372 pci_save_state(pci_dev);
373 pci_dev->state_saved = true;
373 /* 374 /*
374 * This is for compatibility with existing code with legacy PM support. 375 * This is for compatibility with existing code with legacy PM support.
375 */ 376 */
@@ -419,6 +420,7 @@ static int pci_legacy_resume(struct device *dev)
419static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) 420static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev)
420{ 421{
421 pci_restore_standard_config(pci_dev); 422 pci_restore_standard_config(pci_dev);
423 pci_dev->state_saved = false;
422 pci_fixup_device(pci_fixup_resume_early, pci_dev); 424 pci_fixup_device(pci_fixup_resume_early, pci_dev);
423} 425}
424 426
@@ -555,6 +557,13 @@ static int pci_pm_resume(struct device *dev)
555 struct device_driver *drv = dev->driver; 557 struct device_driver *drv = dev->driver;
556 int error = 0; 558 int error = 0;
557 559
560 /*
561 * This is necessary for the suspend error path in which resume is
562 * called without restoring the standard config registers of the device.
563 */
564 if (pci_dev->state_saved)
565 pci_restore_standard_config(pci_dev);
566
558 if (pci_has_legacy_pm_support(pci_dev)) 567 if (pci_has_legacy_pm_support(pci_dev))
559 return pci_legacy_resume(dev); 568 return pci_legacy_resume(dev);
560 569
@@ -710,6 +719,13 @@ static int pci_pm_restore(struct device *dev)
710 struct device_driver *drv = dev->driver; 719 struct device_driver *drv = dev->driver;
711 int error = 0; 720 int error = 0;
712 721
722 /*
723 * This is necessary for the hibernation error path in which restore is
724 * called without restoring the standard config registers of the device.
725 */
726 if (pci_dev->state_saved)
727 pci_restore_standard_config(pci_dev);
728
713 if (pci_has_legacy_pm_support(pci_dev)) 729 if (pci_has_legacy_pm_support(pci_dev))
714 return pci_legacy_resume(dev); 730 return pci_legacy_resume(dev);
715 731