Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6fbc198c authored by Michael S. Tsirkin's avatar Michael S. Tsirkin Committed by Rusty Russell
Browse files

virtio_pci: fix virtio spec compliance on restore



On restore, virtio pci does the following:
+ set features
+ init vqs etc - device can be used at this point!
+ set ACKNOWLEDGE,DRIVER and DRIVER_OK status bits

This is in violation of the virtio spec, which
requires the following order:
- ACKNOWLEDGE
- DRIVER
- init vqs
- DRIVER_OK

This behaviour will break with hypervisors that assume spec compliant
behaviour.  It seems like a good idea to have this patch applied to
stable branches to reduce the support butden for the hypervisors.

Cc: stable@vger.kernel.org
Cc: Amit Shah <amit.shah@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 7ec62d42
Loading
Loading
Loading
Loading
+30 −3
Original line number Diff line number Diff line
@@ -789,6 +789,7 @@ static int virtio_pci_restore(struct device *dev)
	struct pci_dev *pci_dev = to_pci_dev(dev);
	struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
	struct virtio_driver *drv;
	unsigned status = 0;
	int ret;

	drv = container_of(vp_dev->vdev.dev.driver,
@@ -799,14 +800,40 @@ static int virtio_pci_restore(struct device *dev)
		return ret;

	pci_set_master(pci_dev);
	/* We always start by resetting the device, in case a previous
	 * driver messed it up. */
	vp_reset(&vp_dev->vdev);

	/* Acknowledge that we've seen the device. */
	status |= VIRTIO_CONFIG_S_ACKNOWLEDGE;
	vp_set_status(&vp_dev->vdev, status);

	/* Maybe driver failed before freeze.
	 * Restore the failed status, for debugging. */
	status |= vp_dev->saved_status & VIRTIO_CONFIG_S_FAILED;
	vp_set_status(&vp_dev->vdev, status);

	if (!drv)
		return 0;

	/* We have a driver! */
	status |= VIRTIO_CONFIG_S_DRIVER;
	vp_set_status(&vp_dev->vdev, status);

	vp_finalize_features(&vp_dev->vdev);

	if (drv && drv->restore)
	if (drv->restore) {
		ret = drv->restore(&vp_dev->vdev);
		if (ret) {
			status |= VIRTIO_CONFIG_S_FAILED;
			vp_set_status(&vp_dev->vdev, status);
			return ret;
		}
	}

	/* Finally, tell the device we're all set */
	if (!ret)
		vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
	status |= VIRTIO_CONFIG_S_DRIVER_OK;
	vp_set_status(&vp_dev->vdev, status);

	return ret;
}