virtio: drop thaw PM operation
[linux-2.6.git] / drivers / virtio / virtio_pci.c
index ef8d9d5..a35a402 100644 (file)
@@ -55,6 +55,10 @@ struct virtio_pci_device
        unsigned msix_vectors;
        /* 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;
 };
@@ -96,11 +100,6 @@ static struct pci_device_id virtio_pci_id_table[] = {
 
 MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
 
-/* A PCI device has it's own struct device and so does a virtio device so
- * we create a place for the virtio devices to show up in sysfs.  I think it
- * would make more sense for virtio to not insist on having it's own device. */
-static struct device *virtio_pci_root;
-
 /* Convert a generic virtio device to our structure */
 static struct virtio_pci_device *to_vp_device(struct virtio_device *vdev)
 {
@@ -174,11 +173,29 @@ static void vp_set_status(struct virtio_device *vdev, u8 status)
        iowrite8(status, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
 }
 
+/* wait for pending irq handlers */
+static void vp_synchronize_vectors(struct virtio_device *vdev)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       int i;
+
+       if (vp_dev->intx_enabled)
+               synchronize_irq(vp_dev->pci_dev->irq);
+
+       for (i = 0; i < vp_dev->msix_vectors; ++i)
+               synchronize_irq(vp_dev->msix_entries[i].vector);
+}
+
 static void vp_reset(struct virtio_device *vdev)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        /* 0 status means a reset. */
        iowrite8(0, vp_dev->ioaddr + VIRTIO_PCI_STATUS);
+       /* Flush out the status write, and flush in device writes,
+        * including MSi-X interrupts, if any. */
+       ioread8(vp_dev->ioaddr + VIRTIO_PCI_STATUS);
+       /* Flush pending VQ/configuration callbacks. */
+       vp_synchronize_vectors(vdev);
 }
 
 /* the notify function used when creating a virt queue */
@@ -401,8 +418,8 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
                  vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
        /* create the vring */
-       vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
-                                vdev, info->queue, vp_notify, callback, name);
+       vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN, vdev,
+                                true, info->queue, vp_notify, callback, name);
        if (!vq) {
                err = -ENOMEM;
                goto out_activate_queue;
@@ -420,9 +437,13 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
                }
        }
 
-       spin_lock_irqsave(&vp_dev->lock, flags);
-       list_add(&info->node, &vp_dev->virtqueues);
-       spin_unlock_irqrestore(&vp_dev->lock, flags);
+       if (callback) {
+               spin_lock_irqsave(&vp_dev->lock, flags);
+               list_add(&info->node, &vp_dev->virtqueues);
+               spin_unlock_irqrestore(&vp_dev->lock, flags);
+       } else {
+               INIT_LIST_HEAD(&info->node);
+       }
 
        return vq;
 
@@ -581,6 +602,13 @@ static int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
                                  false, false);
 }
 
+static const char *vp_bus_name(struct virtio_device *vdev)
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+
+       return pci_name(vp_dev->pci_dev);
+}
+
 static struct virtio_config_ops virtio_pci_config_ops = {
        .get            = vp_get,
        .set            = vp_set,
@@ -591,20 +619,16 @@ static struct virtio_config_ops virtio_pci_config_ops = {
        .del_vqs        = vp_del_vqs,
        .get_features   = vp_get_features,
        .finalize_features = vp_finalize_features,
+       .bus_name       = vp_bus_name,
 };
 
 static void virtio_pci_release_dev(struct device *_d)
 {
-       struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
-       struct virtio_pci_device *vp_dev = to_vp_device(dev);
-       struct pci_dev *pci_dev = vp_dev->pci_dev;
-
-       vp_del_vqs(dev);
-       pci_set_drvdata(pci_dev, NULL);
-       pci_iounmap(pci_dev, vp_dev->ioaddr);
-       pci_release_regions(pci_dev);
-       pci_disable_device(pci_dev);
-       kfree(vp_dev);
+       /*
+        * No need for a release method as we allocate/free
+        * all devices together with the pci devices.
+        * Provide an empty one to avoid getting a warning from core.
+        */
 }
 
 /* the PCI probing function */
@@ -629,7 +653,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
        if (vp_dev == NULL)
                return -ENOMEM;
 
-       vp_dev->vdev.dev.parent = virtio_pci_root;
+       vp_dev->vdev.dev.parent = &pci_dev->dev;
        vp_dev->vdev.dev.release = virtio_pci_release_dev;
        vp_dev->vdev.config = &virtio_pci_config_ops;
        vp_dev->pci_dev = pci_dev;
@@ -686,22 +710,98 @@ static void __devexit virtio_pci_remove(struct pci_dev *pci_dev)
        struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
 
        unregister_virtio_device(&vp_dev->vdev);
+
+       vp_del_vqs(&vp_dev->vdev);
+       pci_set_drvdata(pci_dev, NULL);
+       pci_iounmap(pci_dev, vp_dev->ioaddr);
+       pci_release_regions(pci_dev);
+       pci_disable_device(pci_dev);
+       kfree(vp_dev);
 }
 
 #ifdef CONFIG_PM
-static int virtio_pci_suspend(struct pci_dev *pci_dev, pm_message_t state)
+static int virtio_pci_suspend(struct device *dev)
 {
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+
        pci_save_state(pci_dev);
        pci_set_power_state(pci_dev, PCI_D3hot);
        return 0;
 }
 
-static int virtio_pci_resume(struct pci_dev *pci_dev)
+static int virtio_pci_resume(struct device *dev)
 {
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+
        pci_restore_state(pci_dev);
        pci_set_power_state(pci_dev, PCI_D0);
        return 0;
 }
+
+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);
+
+       if (!ret)
+               pci_disable_device(pci_dev);
+       return ret;
+}
+
+static int restore_common(struct device *dev)
+{
+       struct pci_dev *pci_dev = to_pci_dev(dev);
+       struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev);
+       int ret;
+
+       ret = pci_enable_device(pci_dev);
+       if (ret)
+               return ret;
+       pci_set_master(pci_dev);
+       vp_finalize_features(&vp_dev->vdev);
+
+       return ret;
+}
+
+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;
+       int ret;
+
+       drv = container_of(vp_dev->vdev.dev.driver,
+                          struct virtio_driver, driver);
+
+       ret = restore_common(dev);
+       if (!ret && drv && drv->restore)
+               ret = drv->restore(&vp_dev->vdev);
+
+       /* Finally, tell the device we're all set */
+       if (!ret)
+               vp_set_status(&vp_dev->vdev, vp_dev->saved_status);
+
+       return ret;
+}
+
+static const struct dev_pm_ops virtio_pci_pm_ops = {
+       .suspend        = virtio_pci_suspend,
+       .resume         = virtio_pci_resume,
+       .freeze         = virtio_pci_freeze,
+       .thaw           = virtio_pci_restore,
+       .restore        = virtio_pci_restore,
+       .poweroff       = virtio_pci_suspend,
+};
 #endif
 
 static struct pci_driver virtio_pci_driver = {
@@ -710,24 +810,13 @@ static struct pci_driver virtio_pci_driver = {
        .probe          = virtio_pci_probe,
        .remove         = __devexit_p(virtio_pci_remove),
 #ifdef CONFIG_PM
-       .suspend        = virtio_pci_suspend,
-       .resume         = virtio_pci_resume,
+       .driver.pm      = &virtio_pci_pm_ops,
 #endif
 };
 
 static int __init virtio_pci_init(void)
 {
-       int err;
-
-       virtio_pci_root = root_device_register("virtio-pci");
-       if (IS_ERR(virtio_pci_root))
-               return PTR_ERR(virtio_pci_root);
-
-       err = pci_register_driver(&virtio_pci_driver);
-       if (err)
-               root_device_unregister(virtio_pci_root);
-
-       return err;
+       return pci_register_driver(&virtio_pci_driver);
 }
 
 module_init(virtio_pci_init);
@@ -735,7 +824,6 @@ module_init(virtio_pci_init);
 static void __exit virtio_pci_exit(void)
 {
        pci_unregister_driver(&virtio_pci_driver);
-       root_device_unregister(virtio_pci_root);
 }
 
 module_exit(virtio_pci_exit);