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
					
				| @ -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: | ||||
|  | ||||
| @ -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; | ||||
| 
 | ||||
| 		info->suspended = false; | ||||
| 		vhost_user_set_vring_enable(vu_dev, vq->index, true); | ||||
| 		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); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	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 New Issue
	
	Block a user