virtio-pci: do not oops on config change if driver not loaded
[safe/jmp/linux-2.6] / drivers / virtio / virtio_pci.c
index c7dc37c..330aacb 100644 (file)
@@ -73,10 +73,7 @@ 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 = {
-       .parent         = NULL,
-       .bus_id         = "virtio-pci",
-};
+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)
@@ -195,7 +192,7 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
                drv = container_of(vp_dev->vdev.dev.driver,
                                   struct virtio_driver, driver);
 
-               if (drv->config_changed)
+               if (drv && drv->config_changed)
                        drv->config_changed(&vp_dev->vdev);
        }
 
@@ -216,7 +213,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        struct virtio_pci_vq_info *info;
        struct virtqueue *vq;
-       unsigned long flags;
+       unsigned long flags, size;
        u16 num;
        int err;
 
@@ -237,19 +234,20 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
        info->queue_index = index;
        info->num = num;
 
-       info->queue = kzalloc(PAGE_ALIGN(vring_size(num,PAGE_SIZE)), GFP_KERNEL);
+       size = PAGE_ALIGN(vring_size(num, VIRTIO_PCI_VRING_ALIGN));
+       info->queue = alloc_pages_exact(size, GFP_KERNEL|__GFP_ZERO);
        if (info->queue == NULL) {
                err = -ENOMEM;
                goto out_info;
        }
 
        /* activate the queue */
-       iowrite32(virt_to_phys(info->queue) >> PAGE_SHIFT,
+       iowrite32(virt_to_phys(info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
                  vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
        /* create the vring */
-       vq = vring_new_virtqueue(info->num, vdev, info->queue,
-                                vp_notify, callback);
+       vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN,
+                                vdev, info->queue, vp_notify, callback);
        if (!vq) {
                err = -ENOMEM;
                goto out_activate_queue;
@@ -266,7 +264,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device *vdev, unsigned index,
 
 out_activate_queue:
        iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
-       kfree(info->queue);
+       free_pages_exact(info->queue, size);
 out_info:
        kfree(info);
        return ERR_PTR(err);
@@ -277,7 +275,7 @@ static void vp_del_vq(struct virtqueue *vq)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
        struct virtio_pci_vq_info *info = vq->priv;
-       unsigned long flags;
+       unsigned long flags, size;
 
        spin_lock_irqsave(&vp_dev->lock, flags);
        list_del(&info->node);
@@ -289,7 +287,8 @@ static void vp_del_vq(struct virtqueue *vq)
        iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
        iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
-       kfree(info->queue);
+       size = PAGE_ALIGN(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN));
+       free_pages_exact(info->queue, size);
        kfree(info);
 }
 
@@ -305,6 +304,20 @@ static struct virtio_config_ops virtio_pci_config_ops = {
        .finalize_features = vp_finalize_features,
 };
 
+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;
+
+       free_irq(pci_dev->irq, vp_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);
+}
+
 /* the PCI probing function */
 static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
                                      const struct pci_device_id *id)
@@ -327,7 +340,8 @@ 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 = virtio_pci_root;
+       vp_dev->vdev.dev.release = virtio_pci_release_dev;
        vp_dev->vdev.config = &virtio_pci_config_ops;
        vp_dev->pci_dev = pci_dev;
        INIT_LIST_HEAD(&vp_dev->virtqueues);
@@ -357,7 +371,7 @@ static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 
        /* register a handler for the queue with the PCI device's interrupt */
        err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
-                         vp_dev->vdev.dev.bus_id, vp_dev);
+                         dev_name(&vp_dev->vdev.dev), vp_dev);
        if (err)
                goto out_set_drvdata;
 
@@ -387,12 +401,6 @@ 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);
-       free_irq(pci_dev->irq, vp_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);
 }
 
 #ifdef CONFIG_PM
@@ -426,13 +434,13 @@ static int __init virtio_pci_init(void)
 {
        int err;
 
-       err = device_register(&virtio_pci_root);
-       if (err)
-               return 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)
-               device_unregister(&virtio_pci_root);
+               device_unregister(virtio_pci_root);
 
        return err;
 }
@@ -441,8 +449,8 @@ module_init(virtio_pci_init);
 
 static void __exit virtio_pci_exit(void)
 {
-       device_unregister(&virtio_pci_root);
        pci_unregister_driver(&virtio_pci_driver);
+       root_device_unregister(virtio_pci_root);
 }
 
 module_exit(virtio_pci_exit);