From 6fbc198cf623944ab60a1db6d306a4d55cdd820d Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 14 Oct 2014 10:40:29 +1030 Subject: 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 Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_pci.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers/virtio') diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index 3d1463c6b120..add40d00dcdb 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -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; } -- cgit v1.2.3 From 016c98c6fe0c914d12e2e242b2bccde6d6dea54b Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 14 Oct 2014 10:40:34 +1030 Subject: virtio: unify config_changed handling Replace duplicated code in all transports with a single wrapper in virtio.c. The only functional change is in virtio_mmio.c: if a buggy device sends us an interrupt before driver is set, we previously returned IRQ_NONE, now we return IRQ_HANDLED. As this must not happen in practice, this does not look like a big deal. See also commit 3fff0179e33cd7d0a688dab65700c46ad089e934 virtio-pci: do not oops on config change if driver not loaded. for the original motivation behind the driver check. Signed-off-by: Michael S. Tsirkin Reviewed-by: Cornelia Huck Signed-off-by: Rusty Russell --- drivers/virtio/virtio.c | 9 +++++++++ drivers/virtio/virtio_mmio.c | 7 ++----- drivers/virtio/virtio_pci.c | 6 +----- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers/virtio') diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index fed0ce198ae3..3980687401f6 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -239,6 +239,15 @@ void unregister_virtio_device(struct virtio_device *dev) } EXPORT_SYMBOL_GPL(unregister_virtio_device); +void virtio_config_changed(struct virtio_device *dev) +{ + struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + + if (drv && drv->config_changed) + drv->config_changed(dev); +} +EXPORT_SYMBOL_GPL(virtio_config_changed); + static int virtio_init(void) { if (bus_register(&virtio_bus) != 0) diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c index c600ccfd6922..ef9a1650bb80 100644 --- a/drivers/virtio/virtio_mmio.c +++ b/drivers/virtio/virtio_mmio.c @@ -234,8 +234,6 @@ static irqreturn_t vm_interrupt(int irq, void *opaque) { struct virtio_mmio_device *vm_dev = opaque; struct virtio_mmio_vq_info *info; - struct virtio_driver *vdrv = container_of(vm_dev->vdev.dev.driver, - struct virtio_driver, driver); unsigned long status; unsigned long flags; irqreturn_t ret = IRQ_NONE; @@ -244,9 +242,8 @@ static irqreturn_t vm_interrupt(int irq, void *opaque) status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); - if (unlikely(status & VIRTIO_MMIO_INT_CONFIG) - && vdrv && vdrv->config_changed) { - vdrv->config_changed(&vm_dev->vdev); + if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) { + virtio_config_changed(&vm_dev->vdev); ret = IRQ_HANDLED; } diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index add40d00dcdb..f39f4e772e6a 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -211,12 +211,8 @@ static bool vp_notify(struct virtqueue *vq) static irqreturn_t vp_config_changed(int irq, void *opaque) { struct virtio_pci_device *vp_dev = opaque; - struct virtio_driver *drv; - drv = container_of(vp_dev->vdev.dev.driver, - struct virtio_driver, driver); - if (drv && drv->config_changed) - drv->config_changed(&vp_dev->vdev); + virtio_config_changed(&vp_dev->vdev); return IRQ_HANDLED; } -- cgit v1.2.3 From c6716bae52f97347e25166c6270aa98693d9212c Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 14 Oct 2014 10:40:35 +1030 Subject: virtio-pci: move freeze/restore to virtio core This is in preparation to extending config changed event handling in core. Wrapping these in an API also seems to make for a cleaner code. Signed-off-by: Michael S. Tsirkin Reviewed-by: Cornelia Huck Signed-off-by: Rusty Russell --- drivers/virtio/virtio.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ drivers/virtio/virtio_pci.c | 54 ++------------------------------------------- 2 files changed, 56 insertions(+), 52 deletions(-) (limited to 'drivers/virtio') diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 3980687401f6..8216b7311092 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -248,6 +248,60 @@ void virtio_config_changed(struct virtio_device *dev) } EXPORT_SYMBOL_GPL(virtio_config_changed); +#ifdef CONFIG_PM_SLEEP +int virtio_device_freeze(struct virtio_device *dev) +{ + struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + + dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; + + if (drv && drv->freeze) + return drv->freeze(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_device_freeze); + +int virtio_device_restore(struct virtio_device *dev) +{ + struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + + /* We always start by resetting the device, in case a previous + * driver messed it up. */ + dev->config->reset(dev); + + /* Acknowledge that we've seen the device. */ + add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); + + /* Maybe driver failed before freeze. + * Restore the failed status, for debugging. */ + if (dev->failed) + add_status(dev, VIRTIO_CONFIG_S_FAILED); + + if (!drv) + return 0; + + /* We have a driver! */ + add_status(dev, VIRTIO_CONFIG_S_DRIVER); + + dev->config->finalize_features(dev); + + if (drv->restore) { + int ret = drv->restore(dev); + if (ret) { + add_status(dev, VIRTIO_CONFIG_S_FAILED); + return ret; + } + } + + /* Finally, tell the device we're all set */ + add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); + + return 0; +} +EXPORT_SYMBOL_GPL(virtio_device_restore); +#endif + static int virtio_init(void) { if (bus_register(&virtio_bus) != 0) diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c index f39f4e772e6a..d34ebfa604f3 100644 --- a/drivers/virtio/virtio_pci.c +++ b/drivers/virtio/virtio_pci.c @@ -57,9 +57,6 @@ struct virtio_pci_device /* Vectors allocated, excluding per-vq vectors if any */ unsigned msix_used_vectors; - /* Status saved during hibernate/restore */ - u8 saved_status; - /* Whether we have vector per vq */ bool per_vq_vectors; }; @@ -764,16 +761,9 @@ static int virtio_pci_freeze(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; int ret; - drv = container_of(vp_dev->vdev.dev.driver, - struct virtio_driver, driver); - - ret = 0; - vp_dev->saved_status = vp_get_status(&vp_dev->vdev); - if (drv && drv->freeze) - ret = drv->freeze(&vp_dev->vdev); + ret = virtio_device_freeze(&vp_dev->vdev); if (!ret) pci_disable_device(pci_dev); @@ -784,54 +774,14 @@ 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, - struct virtio_driver, driver); - ret = pci_enable_device(pci_dev); if (ret) 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->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 */ - status |= VIRTIO_CONFIG_S_DRIVER_OK; - vp_set_status(&vp_dev->vdev, status); - - return ret; + return virtio_device_restore(&vp_dev->vdev); } static const struct dev_pm_ops virtio_pci_pm_ops = { -- cgit v1.2.3 From 22b7050a024d7deb0c9ef1e14ed73e3b1e369f24 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 15 Oct 2014 10:21:55 +1030 Subject: virtio: defer config changed notifications Defer config changed notifications that arrive during probe/scan/freeze/restore. This will allow drivers to set DRIVER_OK earlier, without worrying about racing with config change interrupts. This change will also benefit old hypervisors (before 2009) that send interrupts without checking DRIVER_OK: previously, the callback could race with driver-specific initialization. This will also help simplify drivers. Signed-off-by: Michael S. Tsirkin Reviewed-by: Cornelia Huck Signed-off-by: Rusty Russell (cosmetic changes) --- drivers/virtio/virtio.c | 58 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) (limited to 'drivers/virtio') diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 8216b7311092..df598dd8c5c8 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -117,6 +117,43 @@ void virtio_check_driver_offered_feature(const struct virtio_device *vdev, } EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); +static void __virtio_config_changed(struct virtio_device *dev) +{ + struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + + if (!dev->config_enabled) + dev->config_change_pending = true; + else if (drv && drv->config_changed) + drv->config_changed(dev); +} + +void virtio_config_changed(struct virtio_device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->config_lock, flags); + __virtio_config_changed(dev); + spin_unlock_irqrestore(&dev->config_lock, flags); +} +EXPORT_SYMBOL_GPL(virtio_config_changed); + +static void virtio_config_disable(struct virtio_device *dev) +{ + spin_lock_irq(&dev->config_lock); + dev->config_enabled = false; + spin_unlock_irq(&dev->config_lock); +} + +static void virtio_config_enable(struct virtio_device *dev) +{ + spin_lock_irq(&dev->config_lock); + dev->config_enabled = true; + if (dev->config_change_pending) + __virtio_config_changed(dev); + dev->config_change_pending = false; + spin_unlock_irq(&dev->config_lock); +} + static int virtio_dev_probe(struct device *_d) { int err, i; @@ -153,6 +190,8 @@ static int virtio_dev_probe(struct device *_d) add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); if (drv->scan) drv->scan(dev); + + virtio_config_enable(dev); } return err; @@ -163,6 +202,8 @@ static int virtio_dev_remove(struct device *_d) struct virtio_device *dev = dev_to_virtio(_d); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + virtio_config_disable(dev); + drv->remove(dev); /* Driver should have reset device. */ @@ -211,6 +252,10 @@ int register_virtio_device(struct virtio_device *dev) dev->index = err; dev_set_name(&dev->dev, "virtio%u", dev->index); + spin_lock_init(&dev->config_lock); + dev->config_enabled = false; + dev->config_change_pending = false; + /* We always start by resetting the device, in case a previous * driver messed it up. This also tests that code path a little. */ dev->config->reset(dev); @@ -239,20 +284,13 @@ void unregister_virtio_device(struct virtio_device *dev) } EXPORT_SYMBOL_GPL(unregister_virtio_device); -void virtio_config_changed(struct virtio_device *dev) -{ - struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); - - if (drv && drv->config_changed) - drv->config_changed(dev); -} -EXPORT_SYMBOL_GPL(virtio_config_changed); - #ifdef CONFIG_PM_SLEEP int virtio_device_freeze(struct virtio_device *dev) { struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); + virtio_config_disable(dev); + dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; if (drv && drv->freeze) @@ -297,6 +335,8 @@ int virtio_device_restore(struct virtio_device *dev) /* Finally, tell the device we're all set */ add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); + virtio_config_enable(dev); + return 0; } EXPORT_SYMBOL_GPL(virtio_device_restore); -- cgit v1.2.3 From 486d2e632ca157558a738626c092973f309f3b45 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Wed, 15 Oct 2014 10:22:33 +1030 Subject: virtio_balloon: enable VQs early on restore virtio spec requires drivers to set DRIVER_OK before using VQs. This is set automatically after resume returns, virtio balloon violated this rule by adding bufs, which causes the VQ to be used directly within restore. To fix, call virtio_device_ready before using VQ. Signed-off-by: Michael S. Tsirkin Signed-off-by: Rusty Russell --- drivers/virtio/virtio_balloon.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/virtio') diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 25ebe8eecdb7..f4a28af4865e 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -538,6 +538,8 @@ static int virtballoon_restore(struct virtio_device *vdev) if (ret) return ret; + virtio_device_ready(vdev); + fill_balloon(vb, towards_target(vb)); update_balloon_size(vb); return 0; -- cgit v1.2.3