vfio/pci: Mask INTx during runtime suspend
authorAbhishek Sahu <abhsahu@nvidia.com>
Mon, 29 Aug 2022 11:48:48 +0000 (17:18 +0530)
committerAlex Williamson <alex.williamson@redhat.com>
Thu, 1 Sep 2022 21:29:11 +0000 (15:29 -0600)
This patch adds INTx handling during runtime suspend/resume.
All the suspend/resume related code for the user to put the device
into the low power state will be added in subsequent patches.

The INTx lines may be shared among devices. Whenever any INTx
interrupt comes for the VFIO devices, then vfio_intx_handler() will be
called for each device sharing the interrupt. Inside vfio_intx_handler(),
it calls pci_check_and_mask_intx() and checks if the interrupt has
been generated for the current device. Now, if the device is already
in the D3cold state, then the config space can not be read. Attempt
to read config space in D3cold state can cause system unresponsiveness
in a few systems. To prevent this, mask INTx in runtime suspend callback,
and unmask the same in runtime resume callback. If INTx has been already
masked, then no handling is needed in runtime suspend/resume callbacks.
'pm_intx_masked' tracks this, and vfio_pci_intx_mask() has been updated
to return true if the INTx vfio_pci_irq_ctx.masked value is changed
inside this function.

For the runtime suspend which is triggered for the no user of VFIO
device, the 'irq_type' will be VFIO_PCI_NUM_IRQS and these
callbacks won't do anything.

The MSI/MSI-X are not shared so similar handling should not be
needed for MSI/MSI-X. vfio_msihandler() triggers eventfd_signal()
without doing any device-specific config access. When the user performs
any config access or IOCTL after receiving the eventfd notification,
then the device will be moved to the D0 state first before
servicing any request.

Another option was to check this flag 'pm_intx_masked' inside
vfio_intx_handler() instead of masking the interrupts. This flag
is being set inside the runtime_suspend callback but the device
can be in non-D3cold state (for example, if the user has disabled D3cold
explicitly by sysfs, the D3cold is not supported in the platform, etc.).
Also, in D3cold supported case, the device will be in D0 till the
PCI core moves the device into D3cold. In this case, there is
a possibility that the device can generate an interrupt. Adding check
in the IRQ handler will not clear the IRQ status and the interrupt
line will still be asserted. This can cause interrupt flooding.

Signed-off-by: Abhishek Sahu <abhsahu@nvidia.com>
Link: https://lore.kernel.org/r/20220829114850.4341-4-abhsahu@nvidia.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
drivers/vfio/pci/vfio_pci_core.c
drivers/vfio/pci/vfio_pci_intrs.c
drivers/vfio/pci/vfio_pci_priv.h
include/linux/vfio_pci_core.h

index 9273f1ffd0ddd0e4213c717b1c49121e1d55c78b..207ede189c2af63566384fe237951d058f3ac2d8 100644 (file)
@@ -277,16 +277,46 @@ int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t stat
        return ret;
 }
 
+#ifdef CONFIG_PM
+static int vfio_pci_core_runtime_suspend(struct device *dev)
+{
+       struct vfio_pci_core_device *vdev = dev_get_drvdata(dev);
+
+       /*
+        * If INTx is enabled, then mask INTx before going into the runtime
+        * suspended state and unmask the same in the runtime resume.
+        * If INTx has already been masked by the user, then
+        * vfio_pci_intx_mask() will return false and in that case, INTx
+        * should not be unmasked in the runtime resume.
+        */
+       vdev->pm_intx_masked = ((vdev->irq_type == VFIO_PCI_INTX_IRQ_INDEX) &&
+                               vfio_pci_intx_mask(vdev));
+
+       return 0;
+}
+
+static int vfio_pci_core_runtime_resume(struct device *dev)
+{
+       struct vfio_pci_core_device *vdev = dev_get_drvdata(dev);
+
+       if (vdev->pm_intx_masked)
+               vfio_pci_intx_unmask(vdev);
+
+       return 0;
+}
+#endif /* CONFIG_PM */
+
 /*
- * The dev_pm_ops needs to be provided to make pci-driver runtime PM working,
- * so use structure without any callbacks.
- *
  * The pci-driver core runtime PM routines always save the device state
  * before going into suspended state. If the device is going into low power
  * state with only with runtime PM ops, then no explicit handling is needed
  * for the devices which have NoSoftRst-.
  */
-static const struct dev_pm_ops vfio_pci_core_pm_ops = { };
+static const struct dev_pm_ops vfio_pci_core_pm_ops = {
+       SET_RUNTIME_PM_OPS(vfio_pci_core_runtime_suspend,
+                          vfio_pci_core_runtime_resume,
+                          NULL)
+};
 
 int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
 {
index 8cb987ef3c4763c654eaa16d71ce222fa7c55ab5..40c3d7cf163f69ce0a860e36d583db4b0ea1015b 100644 (file)
@@ -59,10 +59,12 @@ static void vfio_send_intx_eventfd(void *opaque, void *unused)
                eventfd_signal(vdev->ctx[0].trigger, 1);
 }
 
-void vfio_pci_intx_mask(struct vfio_pci_core_device *vdev)
+/* Returns true if the INTx vfio_pci_irq_ctx.masked value is changed. */
+bool vfio_pci_intx_mask(struct vfio_pci_core_device *vdev)
 {
        struct pci_dev *pdev = vdev->pdev;
        unsigned long flags;
+       bool masked_changed = false;
 
        spin_lock_irqsave(&vdev->irqlock, flags);
 
@@ -86,9 +88,11 @@ void vfio_pci_intx_mask(struct vfio_pci_core_device *vdev)
                        disable_irq_nosync(pdev->irq);
 
                vdev->ctx[0].masked = true;
+               masked_changed = true;
        }
 
        spin_unlock_irqrestore(&vdev->irqlock, flags);
+       return masked_changed;
 }
 
 /*
index 58b8d34c162cd617a16f18382ffc74ca8310e2f0..5e4fa69aee16c13966391328440e9353b5e33427 100644 (file)
@@ -23,7 +23,7 @@ struct vfio_pci_ioeventfd {
        bool                    test_mem;
 };
 
-void vfio_pci_intx_mask(struct vfio_pci_core_device *vdev);
+bool vfio_pci_intx_mask(struct vfio_pci_core_device *vdev);
 void vfio_pci_intx_unmask(struct vfio_pci_core_device *vdev);
 
 int vfio_pci_set_irqs_ioctl(struct vfio_pci_core_device *vdev, uint32_t flags,
index e5cf0d3313a69467dcf844068f4c59c97035f21f..a0f1f36e42a2010578f2d5f1bf108efd7502868e 100644 (file)
@@ -78,6 +78,7 @@ struct vfio_pci_core_device {
        bool                    needs_reset;
        bool                    nointx;
        bool                    needs_pm_restore;
+       bool                    pm_intx_masked;
        struct pci_saved_state  *pci_saved_state;
        struct pci_saved_state  *pm_save;
        int                     ioeventfds_nr;