Commit 46939f8b15e44f065d052e89ea4f2adc81fdc740

Authored by Rafael J. Wysocki
1 parent 0128a89cf7

PCI PM: Put devices into low power states during late suspend (rev. 2)

Once we have allowed timer interrupts to be enabled during the late
phase of suspending devices, we are now able to use the generic
pci_set_power_state() to put PCI devices into low power states at
that time.  We can also use some related platform callbacks, like the
ones preparing devices for wake-up, during the late suspend.

Doing this will allow us to avoid the race condition where a device
using shared interrupts is put into a low power state with interrupts
enabled and then an interrupt (for another device) comes in and
confuses its driver.  At the same time, devices that don't support
the native PCI PM or that require some additional, platform-specific
operations to be carried out to put them into low power states will
be handled as appropriate.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Ingo Molnar <mingo@elte.hu>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Showing 1 changed file with 81 additions and 53 deletions Side-by-side Diff

drivers/pci/pci-driver.c
... ... @@ -352,53 +352,60 @@
352 352 {
353 353 struct pci_dev * pci_dev = to_pci_dev(dev);
354 354 struct pci_driver * drv = pci_dev->driver;
355   - int error = 0;
356 355  
  356 + pci_dev->state_saved = false;
  357 +
357 358 if (drv && drv->suspend) {
358 359 pci_power_t prev = pci_dev->current_state;
  360 + int error;
359 361  
360   - pci_dev->state_saved = false;
361   -
362 362 error = drv->suspend(pci_dev, state);
363 363 suspend_report_result(drv->suspend, error);
364 364 if (error)
365 365 return error;
366 366  
367   - if (pci_dev->state_saved)
368   - goto Fixup;
369   -
370   - if (pci_dev->current_state != PCI_D0
  367 + if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
371 368 && pci_dev->current_state != PCI_UNKNOWN) {
372 369 WARN_ONCE(pci_dev->current_state != prev,
373 370 "PCI PM: Device state not saved by %pF\n",
374 371 drv->suspend);
375   - goto Fixup;
376 372 }
377 373 }
378 374  
379   - pci_save_state(pci_dev);
380   - /*
381   - * This is for compatibility with existing code with legacy PM support.
382   - */
383   - pci_pm_set_unknown_state(pci_dev);
384   -
385   - Fixup:
386 375 pci_fixup_device(pci_fixup_suspend, pci_dev);
387 376  
388   - return error;
  377 + return 0;
389 378 }
390 379  
391 380 static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
392 381 {
393 382 struct pci_dev * pci_dev = to_pci_dev(dev);
394 383 struct pci_driver * drv = pci_dev->driver;
395   - int error = 0;
396 384  
397 385 if (drv && drv->suspend_late) {
  386 + pci_power_t prev = pci_dev->current_state;
  387 + int error;
  388 +
398 389 error = drv->suspend_late(pci_dev, state);
399 390 suspend_report_result(drv->suspend_late, error);
  391 + if (error)
  392 + return error;
  393 +
  394 + if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
  395 + && pci_dev->current_state != PCI_UNKNOWN) {
  396 + WARN_ONCE(pci_dev->current_state != prev,
  397 + "PCI PM: Device state not saved by %pF\n",
  398 + drv->suspend_late);
  399 + return 0;
  400 + }
400 401 }
401   - return error;
  402 +
  403 + if (!pci_dev->state_saved)
  404 + pci_save_state(pci_dev);
  405 +
  406 + pci_pm_set_unknown_state(pci_dev);
  407 +
  408 + return 0;
402 409 }
403 410  
404 411 static int pci_legacy_resume_early(struct device *dev)
... ... @@ -460,7 +467,6 @@
460 467 /* Disable non-bridge devices without PM support */
461 468 if (!pci_is_bridge(pci_dev))
462 469 pci_disable_enabled_device(pci_dev);
463   - pci_save_state(pci_dev);
464 470 }
465 471  
466 472 static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev)
467 473  
468 474  
... ... @@ -526,24 +532,14 @@
526 532 if (error)
527 533 return error;
528 534  
529   - if (pci_dev->state_saved)
530   - goto Fixup;
531   -
532   - if (pci_dev->current_state != PCI_D0
  535 + if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
533 536 && pci_dev->current_state != PCI_UNKNOWN) {
534 537 WARN_ONCE(pci_dev->current_state != prev,
535 538 "PCI PM: State of device not saved by %pF\n",
536 539 pm->suspend);
537   - goto Fixup;
538 540 }
539 541 }
540 542  
541   - if (!pci_dev->state_saved) {
542   - pci_save_state(pci_dev);
543   - if (!pci_is_bridge(pci_dev))
544   - pci_prepare_to_sleep(pci_dev);
545   - }
546   -
547 543 Fixup:
548 544 pci_fixup_device(pci_fixup_suspend, pci_dev);
549 545  
550 546  
551 547  
552 548  
... ... @@ -553,21 +549,41 @@
553 549 static int pci_pm_suspend_noirq(struct device *dev)
554 550 {
555 551 struct pci_dev *pci_dev = to_pci_dev(dev);
556   - struct device_driver *drv = dev->driver;
557   - int error = 0;
  552 + struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
558 553  
559 554 if (pci_has_legacy_pm_support(pci_dev))
560 555 return pci_legacy_suspend_late(dev, PMSG_SUSPEND);
561 556  
562   - if (drv && drv->pm && drv->pm->suspend_noirq) {
563   - error = drv->pm->suspend_noirq(dev);
564   - suspend_report_result(drv->pm->suspend_noirq, error);
  557 + if (!pm)
  558 + return 0;
  559 +
  560 + if (pm->suspend_noirq) {
  561 + pci_power_t prev = pci_dev->current_state;
  562 + int error;
  563 +
  564 + error = pm->suspend_noirq(dev);
  565 + suspend_report_result(pm->suspend_noirq, error);
  566 + if (error)
  567 + return error;
  568 +
  569 + if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
  570 + && pci_dev->current_state != PCI_UNKNOWN) {
  571 + WARN_ONCE(pci_dev->current_state != prev,
  572 + "PCI PM: State of device not saved by %pF\n",
  573 + pm->suspend_noirq);
  574 + return 0;
  575 + }
565 576 }
566 577  
567   - if (!error)
568   - pci_pm_set_unknown_state(pci_dev);
  578 + if (!pci_dev->state_saved) {
  579 + pci_save_state(pci_dev);
  580 + if (!pci_is_bridge(pci_dev))
  581 + pci_prepare_to_sleep(pci_dev);
  582 + }
569 583  
570   - return error;
  584 + pci_pm_set_unknown_state(pci_dev);
  585 +
  586 + return 0;
571 587 }
572 588  
573 589 static int pci_pm_resume_noirq(struct device *dev)
... ... @@ -650,9 +666,6 @@
650 666 return error;
651 667 }
652 668  
653   - if (!pci_dev->state_saved)
654   - pci_save_state(pci_dev);
655   -
656 669 return 0;
657 670 }
658 671  
659 672  
660 673  
661 674  
662 675  
... ... @@ -660,20 +673,25 @@
660 673 {
661 674 struct pci_dev *pci_dev = to_pci_dev(dev);
662 675 struct device_driver *drv = dev->driver;
663   - int error = 0;
664 676  
665 677 if (pci_has_legacy_pm_support(pci_dev))
666 678 return pci_legacy_suspend_late(dev, PMSG_FREEZE);
667 679  
668 680 if (drv && drv->pm && drv->pm->freeze_noirq) {
  681 + int error;
  682 +
669 683 error = drv->pm->freeze_noirq(dev);
670 684 suspend_report_result(drv->pm->freeze_noirq, error);
  685 + if (error)
  686 + return error;
671 687 }
672 688  
673   - if (!error)
674   - pci_pm_set_unknown_state(pci_dev);
  689 + if (!pci_dev->state_saved)
  690 + pci_save_state(pci_dev);
675 691  
676   - return error;
  692 + pci_pm_set_unknown_state(pci_dev);
  693 +
  694 + return 0;
677 695 }
678 696  
679 697 static int pci_pm_thaw_noirq(struct device *dev)
... ... @@ -716,7 +734,6 @@
716 734 {
717 735 struct pci_dev *pci_dev = to_pci_dev(dev);
718 736 struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
719   - int error = 0;
720 737  
721 738 if (pci_has_legacy_pm_support(pci_dev))
722 739 return pci_legacy_suspend(dev, PMSG_HIBERNATE);
723 740  
724 741  
725 742  
726 743  
727 744  
728 745  
729 746  
730 747  
... ... @@ -729,33 +746,44 @@
729 746 pci_dev->state_saved = false;
730 747  
731 748 if (pm->poweroff) {
  749 + int error;
  750 +
732 751 error = pm->poweroff(dev);
733 752 suspend_report_result(pm->poweroff, error);
  753 + if (error)
  754 + return error;
734 755 }
735 756  
736   - if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
737   - pci_prepare_to_sleep(pci_dev);
738   -
739 757 Fixup:
740 758 pci_fixup_device(pci_fixup_suspend, pci_dev);
741 759  
742   - return error;
  760 + return 0;
743 761 }
744 762  
745 763 static int pci_pm_poweroff_noirq(struct device *dev)
746 764 {
  765 + struct pci_dev *pci_dev = to_pci_dev(dev);
747 766 struct device_driver *drv = dev->driver;
748   - int error = 0;
749 767  
750 768 if (pci_has_legacy_pm_support(to_pci_dev(dev)))
751 769 return pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
752 770  
753   - if (drv && drv->pm && drv->pm->poweroff_noirq) {
  771 + if (!drv || !drv->pm)
  772 + return 0;
  773 +
  774 + if (drv->pm->poweroff_noirq) {
  775 + int error;
  776 +
754 777 error = drv->pm->poweroff_noirq(dev);
755 778 suspend_report_result(drv->pm->poweroff_noirq, error);
  779 + if (error)
  780 + return error;
756 781 }
757 782  
758   - return error;
  783 + if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
  784 + pci_prepare_to_sleep(pci_dev);
  785 +
  786 + return 0;
759 787 }
760 788  
761 789 static int pci_pm_restore_noirq(struct device *dev)