]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
um: virtio/pci: enable suspend/resume
authorJohannes Berg <johannes.berg@intel.com>
Fri, 5 Mar 2021 12:19:59 +0000 (13:19 +0100)
committerRichard Weinberger <richard@nod.at>
Thu, 17 Jun 2021 19:45:44 +0000 (21:45 +0200)
The UM virtual PCI devices currently cannot be suspended properly
since the virtio driver already disables VQs well before the PCI
bus's suspend_noirq wants to complete the transition by writing to
PCI config space.

After trying around for a long time with moving the devices on the
DPM list, trying to create dependencies between them, etc. I gave
up and instead added UML specific cross-driver API that lets the
virt-pci code enable not suspending/resuming VQs for its devices.

This then allows the PCI bus suspend_noirq to still talk to the
device, and suspend/resume works properly.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
arch/um/drivers/virt-pci.c
arch/um/drivers/virtio_uml.c
arch/um/include/linux/virtio-uml.h [new file with mode: 0644]

index dd85f36197aadd64f20ee750e7ef184f1efca273..0b802834f40ab3c114f382ca34214278d4622f10 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/logic_iomem.h>
 #include <linux/irqdomain.h>
 #include <linux/virtio_pcidev.h>
+#include <linux/virtio-uml.h>
 #include <linux/delay.h>
 #include <linux/msi.h>
 #include <asm/unaligned.h>
@@ -134,6 +135,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
                if (completed == HANDLE_NO_FREE(cmd))
                        break;
 
+               if (completed && !HANDLE_IS_NO_FREE(completed))
+                       kfree(completed);
+
                if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) ||
                              ++delay_count > UM_VIRT_PCI_MAXDELAY,
                              "um virt-pci delay: %d", delay_count)) {
@@ -550,6 +554,12 @@ static int um_pci_virtio_probe(struct virtio_device *vdev)
 
        device_set_wakeup_enable(&vdev->dev, true);
 
+       /*
+        * In order to do suspend-resume properly, don't allow VQs
+        * to be suspended.
+        */
+       virtio_uml_set_no_vq_suspend(vdev, true);
+
        um_pci_rescan();
        return 0;
 error:
index 91ddf74ca888b0b4347eb7a8b397886608db38ad..4412d6febadef2851f0ffc29866104a382a34390 100644 (file)
@@ -56,6 +56,7 @@ struct virtio_uml_device {
        u8 status;
        u8 registered:1;
        u8 suspended:1;
+       u8 no_vq_suspend:1;
 
        u8 config_changed_irq:1;
        uint64_t vq_irq_vq_map;
@@ -1098,6 +1099,19 @@ static void virtio_uml_release_dev(struct device *d)
        kfree(vu_dev);
 }
 
+void virtio_uml_set_no_vq_suspend(struct virtio_device *vdev,
+                                 bool no_vq_suspend)
+{
+       struct virtio_uml_device *vu_dev = to_virtio_uml_device(vdev);
+
+       if (WARN_ON(vdev->config != &virtio_uml_config_ops))
+               return;
+
+       vu_dev->no_vq_suspend = no_vq_suspend;
+       dev_info(&vdev->dev, "%sabled VQ suspend\n",
+                no_vq_suspend ? "dis" : "en");
+}
+
 /* Platform device */
 
 static int virtio_uml_probe(struct platform_device *pdev)
@@ -1302,13 +1316,16 @@ MODULE_DEVICE_TABLE(of, virtio_uml_match);
 static int virtio_uml_suspend(struct platform_device *pdev, pm_message_t state)
 {
        struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev);
-       struct virtqueue *vq;
 
-       virtio_device_for_each_vq((&vu_dev->vdev), vq) {
-               struct virtio_uml_vq_info *info = vq->priv;
+       if (!vu_dev->no_vq_suspend) {
+               struct virtqueue *vq;
 
-               info->suspended = true;
-               vhost_user_set_vring_enable(vu_dev, vq->index, false);
+               virtio_device_for_each_vq((&vu_dev->vdev), vq) {
+                       struct virtio_uml_vq_info *info = vq->priv;
+
+                       info->suspended = true;
+                       vhost_user_set_vring_enable(vu_dev, vq->index, false);
+               }
        }
 
        if (!device_may_wakeup(&vu_dev->vdev.dev)) {
@@ -1322,13 +1339,16 @@ static int virtio_uml_suspend(struct platform_device *pdev, pm_message_t state)
 static int virtio_uml_resume(struct platform_device *pdev)
 {
        struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev);
-       struct virtqueue *vq;
 
-       virtio_device_for_each_vq((&vu_dev->vdev), vq) {
-               struct virtio_uml_vq_info *info = vq->priv;
+       if (!vu_dev->no_vq_suspend) {
+               struct virtqueue *vq;
+
+               virtio_device_for_each_vq((&vu_dev->vdev), vq) {
+                       struct virtio_uml_vq_info *info = vq->priv;
 
-               info->suspended = false;
-               vhost_user_set_vring_enable(vu_dev, vq->index, true);
+                       info->suspended = false;
+                       vhost_user_set_vring_enable(vu_dev, vq->index, true);
+               }
        }
 
        vu_dev->suspended = false;
diff --git a/arch/um/include/linux/virtio-uml.h b/arch/um/include/linux/virtio-uml.h
new file mode 100644 (file)
index 0000000..2f652fa
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 Intel Corporation
+ * Author: Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#ifndef __VIRTIO_UML_H__
+#define __VIRTIO_UML_H__
+
+void virtio_uml_set_no_vq_suspend(struct virtio_device *vdev,
+                                 bool no_vq_suspend);
+
+#endif /* __VIRTIO_UML_H__ */