aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vfio/pci/vfio_pci_config.c
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2013-02-18 12:10:33 -0500
committerAlex Williamson <alex.williamson@redhat.com>2013-02-18 12:10:33 -0500
commit2dd1194833de133960f286903ce704cb10fa7eb0 (patch)
treea892a48ff846ed92dd49aa50b53a221b503876be /drivers/vfio/pci/vfio_pci_config.c
parent2b489a45f63102205cece37057c21f6fa66f6ce4 (diff)
vfio-pci: Manage user power state transitions
We give the user access to change the power state of the device but certain transitions result in an uninitialized state which the user cannot resolve. To fix this we need to mark the PowerState field of the PMCSR register read-only and effect the requested change on behalf of the user. This has the added benefit that pdev->current_state remains accurate while controlled by the user. The primary example of this bug is a QEMU guest doing a reboot where the device it put into D3 on shutdown and becomes unusable on the next boot because the device did a soft reset on D3->D0 (NoSoftRst-). Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Diffstat (limited to 'drivers/vfio/pci/vfio_pci_config.c')
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c41
1 files changed, 38 insertions, 3 deletions
diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c
index f1dde2c0fe99..964ff22bf281 100644
--- a/drivers/vfio/pci/vfio_pci_config.c
+++ b/drivers/vfio/pci/vfio_pci_config.c
@@ -587,12 +587,46 @@ static int __init init_pci_cap_basic_perm(struct perm_bits *perm)
587 return 0; 587 return 0;
588} 588}
589 589
590static int vfio_pm_config_write(struct vfio_pci_device *vdev, int pos,
591 int count, struct perm_bits *perm,
592 int offset, __le32 val)
593{
594 count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
595 if (count < 0)
596 return count;
597
598 if (offset == PCI_PM_CTRL) {
599 pci_power_t state;
600
601 switch (le32_to_cpu(val) & PCI_PM_CTRL_STATE_MASK) {
602 case 0:
603 state = PCI_D0;
604 break;
605 case 1:
606 state = PCI_D1;
607 break;
608 case 2:
609 state = PCI_D2;
610 break;
611 case 3:
612 state = PCI_D3hot;
613 break;
614 }
615
616 pci_set_power_state(vdev->pdev, state);
617 }
618
619 return count;
620}
621
590/* Permissions for the Power Management capability */ 622/* Permissions for the Power Management capability */
591static int __init init_pci_cap_pm_perm(struct perm_bits *perm) 623static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
592{ 624{
593 if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM])) 625 if (alloc_perm_bits(perm, pci_cap_length[PCI_CAP_ID_PM]))
594 return -ENOMEM; 626 return -ENOMEM;
595 627
628 perm->writefn = vfio_pm_config_write;
629
596 /* 630 /*
597 * We always virtualize the next field so we can remove 631 * We always virtualize the next field so we can remove
598 * capabilities from the chain if we want to. 632 * capabilities from the chain if we want to.
@@ -600,10 +634,11 @@ static int __init init_pci_cap_pm_perm(struct perm_bits *perm)
600 p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE); 634 p_setb(perm, PCI_CAP_LIST_NEXT, (u8)ALL_VIRT, NO_WRITE);
601 635
602 /* 636 /*
603 * Power management is defined *per function*, 637 * Power management is defined *per function*, so we can let
604 * so we let the user write this 638 * the user change power state, but we trap and initiate the
639 * change ourselves, so the state bits are read-only.
605 */ 640 */
606 p_setd(perm, PCI_PM_CTRL, NO_VIRT, ALL_WRITE); 641 p_setd(perm, PCI_PM_CTRL, NO_VIRT, ~PCI_PM_CTRL_STATE_MASK);
607 return 0; 642 return 0;
608} 643}
609 644