mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-29 22:02:02 +00:00
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>
This commit is contained in:
parent
68f5d3f3b6
commit
43c590cb86
3 changed files with 53 additions and 10 deletions
|
@ -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,
|
||||||
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)
|
||||||
|
|
||||||
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 {
|
||||||
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)
|
||||||
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);
|
||||||
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)
|
||||||
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;
|
||||||
|
|
||||||
info->suspended = false;
|
virtio_device_for_each_vq((&vu_dev->vdev), vq) {
|
||||||
vhost_user_set_vring_enable(vu_dev, vq->index, true);
|
struct virtio_uml_vq_info *info = vq->priv;
|
||||||
|
|
||||||
|
info->suspended = false;
|
||||||
|
vhost_user_set_vring_enable(vu_dev, vq->index, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vu_dev->suspended = false;
|
vu_dev->suspended = false;
|
||||||
|
|
13
arch/um/include/linux/virtio-uml.h
Normal file
13
arch/um/include/linux/virtio-uml.h
Normal file
|
@ -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__ */
|
Loading…
Reference in a new issue