Commit 43c590cb authored by Johannes Berg's avatar Johannes Berg Committed by Richard Weinberger

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: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 68f5d3f3
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/logic_iomem.h> #include <linux/logic_iomem.h>
#include <linux/irqdomain.h> #include <linux/irqdomain.h>
#include <linux/virtio_pcidev.h> #include <linux/virtio_pcidev.h>
#include <linux/virtio-uml.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/msi.h> #include <linux/msi.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -134,6 +135,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev, ...@@ -134,6 +135,9 @@ static int um_pci_send_cmd(struct um_pci_device *dev,
if (completed == HANDLE_NO_FREE(cmd)) if (completed == HANDLE_NO_FREE(cmd))
break; break;
if (completed && !HANDLE_IS_NO_FREE(completed))
kfree(completed);
if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) || if (WARN_ONCE(virtqueue_is_broken(dev->cmd_vq) ||
++delay_count > UM_VIRT_PCI_MAXDELAY, ++delay_count > UM_VIRT_PCI_MAXDELAY,
"um virt-pci delay: %d", delay_count)) { "um virt-pci delay: %d", delay_count)) {
...@@ -550,6 +554,12 @@ static int um_pci_virtio_probe(struct virtio_device *vdev) ...@@ -550,6 +554,12 @@ static int um_pci_virtio_probe(struct virtio_device *vdev)
device_set_wakeup_enable(&vdev->dev, true); 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(); um_pci_rescan();
return 0; return 0;
error: error:
......
...@@ -56,6 +56,7 @@ struct virtio_uml_device { ...@@ -56,6 +56,7 @@ struct virtio_uml_device {
u8 status; u8 status;
u8 registered:1; u8 registered:1;
u8 suspended:1; u8 suspended:1;
u8 no_vq_suspend:1;
u8 config_changed_irq:1; u8 config_changed_irq:1;
uint64_t vq_irq_vq_map; uint64_t vq_irq_vq_map;
...@@ -1098,6 +1099,19 @@ static void virtio_uml_release_dev(struct device *d) ...@@ -1098,6 +1099,19 @@ static void virtio_uml_release_dev(struct device *d)
kfree(vu_dev); 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 */ /* Platform device */
static int virtio_uml_probe(struct platform_device *pdev) static int virtio_uml_probe(struct platform_device *pdev)
...@@ -1302,13 +1316,16 @@ MODULE_DEVICE_TABLE(of, virtio_uml_match); ...@@ -1302,13 +1316,16 @@ MODULE_DEVICE_TABLE(of, virtio_uml_match);
static int virtio_uml_suspend(struct platform_device *pdev, pm_message_t state) static int virtio_uml_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev); struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev);
struct virtqueue *vq;
virtio_device_for_each_vq((&vu_dev->vdev), vq) { if (!vu_dev->no_vq_suspend) {
struct virtio_uml_vq_info *info = vq->priv; struct virtqueue *vq;
info->suspended = true; virtio_device_for_each_vq((&vu_dev->vdev), vq) {
vhost_user_set_vring_enable(vu_dev, vq->index, false); 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)) { 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) ...@@ -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) static int virtio_uml_resume(struct platform_device *pdev)
{ {
struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev); struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev);
struct virtqueue *vq;
virtio_device_for_each_vq((&vu_dev->vdev), vq) { if (!vu_dev->no_vq_suspend) {
struct virtio_uml_vq_info *info = vq->priv; struct virtqueue *vq;
virtio_device_for_each_vq((&vu_dev->vdev), vq) {
struct virtio_uml_vq_info *info = vq->priv;
info->suspended = false; info->suspended = false;
vhost_user_set_vring_enable(vu_dev, vq->index, true); vhost_user_set_vring_enable(vu_dev, vq->index, true);
}
} }
vu_dev->suspended = false; vu_dev->suspended = false;
......
/* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment