summaryrefslogtreecommitdiff
path: root/arch/um/drivers
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2021-03-05 13:19:59 +0100
committerRichard Weinberger <richard@nod.at>2021-06-17 21:45:44 +0200
commit43c590cb86665be702c0af0231a10ec813df9cfd (patch)
treedc1c8389fce0e298bf113e880eb457e0bd69de5e /arch/um/drivers
parent68f5d3f3b6543266b29e047cfaf9842333019b4c (diff)
um: virtio/pci: enable suspend/resume
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>
Diffstat (limited to 'arch/um/drivers')
-rw-r--r--arch/um/drivers/virt-pci.c10
-rw-r--r--arch/um/drivers/virtio_uml.c40
2 files changed, 40 insertions, 10 deletions
diff --git a/arch/um/drivers/virt-pci.c b/arch/um/drivers/virt-pci.c
index dd85f36197aa..0b802834f40a 100644
--- a/arch/um/drivers/virt-pci.c
+++ b/arch/um/drivers/virt-pci.c
@@ -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:
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index 91ddf74ca888..4412d6febade 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -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;