From: Nate L. <na...@ro...> - 2004-08-06 16:39:41
|
Matthew Garrett wrote: > On Thu, 2004-08-05 at 14:49 -0400, Nathan Bryant wrote: >>The ELCR save/restore patch is causing regressions for some people. What >>if the problem is that we're saving/restoring registers at the wrong time? > > > On closer investigation, I found that the system was restoring my ioapic > state /after/ it had started the programmable interrupt timer back up. > Changing line 310 of drivers/base/sys.c to list_for_each_entry_reverse > seems to have greatly improved my ability to suspend and resume (which > worked fine before the interrupt controller suspend/resume patches, > except that I didn't get many interrupts...) > > There doesn't seem to be any fine-grained way to control the order of > suspend/resume for individual drivers. It seems to be assumed that > everything that devices depend on will be higher than them in the tree, > and so everything will "just work" - I'm not sure this is true. As > another example, I just had my wireless card fail to resume correctly. > It tried to do a hotplug firmware load on resume. Which was difficult, > because it was resumed before the IDE interface was. How can this sort > of thing be avoided? With a real device framework. We (FreeBSD) propagate suspend requests throough our generic device structure. Each parent and child has a method which can be overridden. This starts at the root and is propagated downward. For instance, the PCI driver has methods that save/restore BARs: DEVMETHOD(device_suspend, pci_suspend), DEVMETHOD(device_resume, pci_resume), The suspend function does its work and then calls bus_generic_suspend() which propagates the request down to the children. Or if it knows that it needs to run after all children have suspended, it calls bus_generic_suspend() first, then does its work. /** * @brief Helper function for implementing DEVICE_SUSPEND() * * This function can be used to help implement the DEVICE_SUSPEND() * for a bus. It calls DEVICE_SUSPEND() for each of the device's * children. If any call to DEVICE_SUSPEND() fails, the suspend * operation is aborted and any devices which were suspended are * resumed immediately by calling their DEVICE_RESUME() methods. */ int bus_generic_suspend(device_t dev) { int error; device_t child, child2; TAILQ_FOREACH(child, &dev->children, link) { error = DEVICE_SUSPEND(child); if (error) { for (child2 = TAILQ_FIRST(&dev->children); child2 && child2 != child; child2 = TAILQ_NEXT(child2, link)) DEVICE_RESUME(child2); return (error); } } return (0); } The key helpful thing is that each driver can specify its own methods through its device object but they can be called in a generic way. For instance, if the root bus does "device_suspend(pci_dev)", then the actual function call expanded at run time is "pci_suspend(pci_dev)" -Nate |