OSDN Git Service

Merge tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Oct 2014 17:25:09 +0000 (10:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 18 Oct 2014 17:25:09 +0000 (10:25 -0700)
Pull virtio updates from Rusty Russell:
 "One cc: stable commit, the rest are a series of minor cleanups which
  have been sitting in MST's tree during my vacation.  I changed a
  function name and made one trivial change, then they spent two days in
  linux-next"

* tag 'virtio-next-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rusty/linux: (25 commits)
  virtio-rng: refactor probe error handling
  virtio_scsi: drop scan callback
  virtio_balloon: enable VQs early on restore
  virtio_scsi: fix race on device removal
  virito_scsi: use freezable WQ for events
  virtio_net: enable VQs early on restore
  virtio_console: enable VQs early on restore
  virtio_scsi: enable VQs early on restore
  virtio_blk: enable VQs early on restore
  virtio_scsi: move kick event out from virtscsi_init
  virtio_net: fix use after free on allocation failure
  9p/trans_virtio: enable VQs early
  virtio_console: enable VQs early
  virtio_blk: enable VQs early
  virtio_net: enable VQs early
  virtio: add API to enable VQs early
  virtio_net: minor cleanup
  virtio-net: drop config_mutex
  virtio_net: drop config_enable
  virtio-blk: drop config_mutex
  ...

15 files changed:
drivers/block/virtio_blk.c
drivers/char/hw_random/virtio-rng.c
drivers/char/virtio_console.c
drivers/misc/mic/card/mic_virtio.c
drivers/net/virtio_net.c
drivers/s390/kvm/kvm_virtio.c
drivers/s390/kvm/virtio_ccw.c
drivers/scsi/virtio_scsi.c
drivers/virtio/virtio.c
drivers/virtio/virtio_balloon.c
drivers/virtio/virtio_mmio.c
drivers/virtio/virtio_pci.c
include/linux/virtio.h
include/linux/virtio_config.h
net/9p/trans_virtio.c

index 0a58140..930fee8 100644 (file)
@@ -41,12 +41,6 @@ struct virtio_blk
        /* Process context for config space updates */
        struct work_struct config_work;
 
-       /* Lock for config space updates */
-       struct mutex config_lock;
-
-       /* enable config space updates */
-       bool config_enable;
-
        /* What host tells us, plus 2 for header & tailer. */
        unsigned int sg_elems;
 
@@ -347,10 +341,6 @@ static void virtblk_config_changed_work(struct work_struct *work)
        char *envp[] = { "RESIZE=1", NULL };
        u64 capacity, size;
 
-       mutex_lock(&vblk->config_lock);
-       if (!vblk->config_enable)
-               goto done;
-
        /* Host must always specify the capacity. */
        virtio_cread(vdev, struct virtio_blk_config, capacity, &capacity);
 
@@ -374,8 +364,6 @@ static void virtblk_config_changed_work(struct work_struct *work)
        set_capacity(vblk->disk, capacity);
        revalidate_disk(vblk->disk);
        kobject_uevent_env(&disk_to_dev(vblk->disk)->kobj, KOBJ_CHANGE, envp);
-done:
-       mutex_unlock(&vblk->config_lock);
 }
 
 static void virtblk_config_changed(struct virtio_device *vdev)
@@ -606,10 +594,8 @@ static int virtblk_probe(struct virtio_device *vdev)
 
        vblk->vdev = vdev;
        vblk->sg_elems = sg_elems;
-       mutex_init(&vblk->config_lock);
 
        INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
-       vblk->config_enable = true;
 
        err = init_vq(vblk);
        if (err)
@@ -733,6 +719,8 @@ static int virtblk_probe(struct virtio_device *vdev)
        if (!err && opt_io_size)
                blk_queue_io_opt(q, blk_size * opt_io_size);
 
+       virtio_device_ready(vdev);
+
        add_disk(vblk->disk);
        err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial);
        if (err)
@@ -771,10 +759,8 @@ static void virtblk_remove(struct virtio_device *vdev)
        int index = vblk->index;
        int refc;
 
-       /* Prevent config work handler from accessing the device. */
-       mutex_lock(&vblk->config_lock);
-       vblk->config_enable = false;
-       mutex_unlock(&vblk->config_lock);
+       /* Make sure no work handler is accessing the device. */
+       flush_work(&vblk->config_work);
 
        del_gendisk(vblk->disk);
        blk_cleanup_queue(vblk->disk->queue);
@@ -784,8 +770,6 @@ static void virtblk_remove(struct virtio_device *vdev)
        /* Stop all the virtqueues. */
        vdev->config->reset(vdev);
 
-       flush_work(&vblk->config_work);
-
        refc = atomic_read(&disk_to_dev(vblk->disk)->kobj.kref.refcount);
        put_disk(vblk->disk);
        vdev->config->del_vqs(vdev);
@@ -805,11 +789,7 @@ static int virtblk_freeze(struct virtio_device *vdev)
        /* Ensure we don't receive any more interrupts */
        vdev->config->reset(vdev);
 
-       /* Prevent config work handler from accessing the device. */
-       mutex_lock(&vblk->config_lock);
-       vblk->config_enable = false;
-       mutex_unlock(&vblk->config_lock);
-
+       /* Make sure no work handler is accessing the device. */
        flush_work(&vblk->config_work);
 
        blk_mq_stop_hw_queues(vblk->disk->queue);
@@ -823,12 +803,14 @@ static int virtblk_restore(struct virtio_device *vdev)
        struct virtio_blk *vblk = vdev->priv;
        int ret;
 
-       vblk->config_enable = true;
        ret = init_vq(vdev->priv);
-       if (!ret)
-               blk_mq_start_stopped_hw_queues(vblk->disk->queue, true);
+       if (ret)
+               return ret;
 
-       return ret;
+       virtio_device_ready(vdev);
+
+       blk_mq_start_stopped_hw_queues(vblk->disk->queue, true);
+       return 0;
 }
 #endif
 
index 132c9cc..72295ea 100644 (file)
@@ -109,8 +109,8 @@ static int probe_common(struct virtio_device *vdev)
 
        vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
        if (index < 0) {
-               kfree(vi);
-               return index;
+               err = index;
+               goto err_ida;
        }
        sprintf(vi->name, "virtio_rng.%d", index);
        init_completion(&vi->have_data);
@@ -128,13 +128,16 @@ static int probe_common(struct virtio_device *vdev)
        vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
        if (IS_ERR(vi->vq)) {
                err = PTR_ERR(vi->vq);
-               vi->vq = NULL;
-               kfree(vi);
-               ida_simple_remove(&rng_index_ida, index);
-               return err;
+               goto err_find;
        }
 
        return 0;
+
+err_find:
+       ida_simple_remove(&rng_index_ida, index);
+err_ida:
+       kfree(vi);
+       return err;
 }
 
 static void remove_common(struct virtio_device *vdev)
index b585b47..bfa6400 100644 (file)
@@ -1449,6 +1449,8 @@ static int add_port(struct ports_device *portdev, u32 id)
        spin_lock_init(&port->outvq_lock);
        init_waitqueue_head(&port->waitqueue);
 
+       virtio_device_ready(portdev->vdev);
+
        /* Fill the in_vq with buffers so the host can send us data. */
        nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock);
        if (!nr_added_bufs) {
@@ -2182,6 +2184,8 @@ static int virtcons_restore(struct virtio_device *vdev)
        if (ret)
                return ret;
 
+       virtio_device_ready(portdev->vdev);
+
        if (use_multiport(portdev))
                fill_queue(portdev->c_ivq, &portdev->c_ivq_lock);
 
index f14b600..e647947 100644 (file)
@@ -462,16 +462,12 @@ static void mic_handle_config_change(struct mic_device_desc __iomem *d,
        struct mic_device_ctrl __iomem *dc
                = (void __iomem *)d + mic_aligned_desc_size(d);
        struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev);
-       struct virtio_driver *drv;
 
        if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED)
                return;
 
        dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__);
-       drv = container_of(mvdev->vdev.dev.driver,
-                               struct virtio_driver, driver);
-       if (drv->config_changed)
-               drv->config_changed(&mvdev->vdev);
+       virtio_config_changed(&mvdev->vdev);
        iowrite8(1, &dc->guest_ack);
 }
 
index 13d0a8b..d75256b 100644 (file)
@@ -123,9 +123,6 @@ struct virtnet_info {
        /* Host can handle any s/g split between our header and packet data */
        bool any_header_sg;
 
-       /* enable config space updates */
-       bool config_enable;
-
        /* Active statistics */
        struct virtnet_stats __percpu *stats;
 
@@ -135,9 +132,6 @@ struct virtnet_info {
        /* Work struct for config space updates */
        struct work_struct config_work;
 
-       /* Lock for config space updates */
-       struct mutex config_lock;
-
        /* Does the affinity hint is set for virtqueues? */
        bool affinity_hint_set;
 
@@ -1414,13 +1408,9 @@ static void virtnet_config_changed_work(struct work_struct *work)
                container_of(work, struct virtnet_info, config_work);
        u16 v;
 
-       mutex_lock(&vi->config_lock);
-       if (!vi->config_enable)
-               goto done;
-
        if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS,
                                 struct virtio_net_config, status, &v) < 0)
-               goto done;
+               return;
 
        if (v & VIRTIO_NET_S_ANNOUNCE) {
                netdev_notify_peers(vi->dev);
@@ -1431,7 +1421,7 @@ static void virtnet_config_changed_work(struct work_struct *work)
        v &= VIRTIO_NET_S_LINK_UP;
 
        if (vi->status == v)
-               goto done;
+               return;
 
        vi->status = v;
 
@@ -1442,8 +1432,6 @@ static void virtnet_config_changed_work(struct work_struct *work)
                netif_carrier_off(vi->dev);
                netif_tx_stop_all_queues(vi->dev);
        }
-done:
-       mutex_unlock(&vi->config_lock);
 }
 
 static void virtnet_config_changed(struct virtio_device *vdev)
@@ -1764,8 +1752,6 @@ static int virtnet_probe(struct virtio_device *vdev)
                u64_stats_init(&virtnet_stats->rx_syncp);
        }
 
-       mutex_init(&vi->config_lock);
-       vi->config_enable = true;
        INIT_WORK(&vi->config_work, virtnet_config_changed_work);
 
        /* If we can receive ANY GSO packets, we must allocate large ones. */
@@ -1813,6 +1799,8 @@ static int virtnet_probe(struct virtio_device *vdev)
                goto free_vqs;
        }
 
+       virtio_device_ready(vdev);
+
        /* Last of all, set up some receive buffers. */
        for (i = 0; i < vi->curr_queue_pairs; i++) {
                try_fill_recv(&vi->rq[i], GFP_KERNEL);
@@ -1849,6 +1837,8 @@ static int virtnet_probe(struct virtio_device *vdev)
        return 0;
 
 free_recv_bufs:
+       vi->vdev->config->reset(vdev);
+
        free_receive_bufs(vi);
        unregister_netdev(dev);
 free_vqs:
@@ -1882,17 +1872,13 @@ static void virtnet_remove(struct virtio_device *vdev)
 
        unregister_hotcpu_notifier(&vi->nb);
 
-       /* Prevent config work handler from accessing the device. */
-       mutex_lock(&vi->config_lock);
-       vi->config_enable = false;
-       mutex_unlock(&vi->config_lock);
+       /* Make sure no work handler is accessing the device. */
+       flush_work(&vi->config_work);
 
        unregister_netdev(vi->dev);
 
        remove_vq_common(vi);
 
-       flush_work(&vi->config_work);
-
        free_percpu(vi->stats);
        free_netdev(vi->dev);
 }
@@ -1905,10 +1891,8 @@ static int virtnet_freeze(struct virtio_device *vdev)
 
        unregister_hotcpu_notifier(&vi->nb);
 
-       /* Prevent config work handler from accessing the device */
-       mutex_lock(&vi->config_lock);
-       vi->config_enable = false;
-       mutex_unlock(&vi->config_lock);
+       /* Make sure no work handler is accessing the device */
+       flush_work(&vi->config_work);
 
        netif_device_detach(vi->dev);
        cancel_delayed_work_sync(&vi->refill);
@@ -1923,8 +1907,6 @@ static int virtnet_freeze(struct virtio_device *vdev)
 
        remove_vq_common(vi);
 
-       flush_work(&vi->config_work);
-
        return 0;
 }
 
@@ -1937,6 +1919,8 @@ static int virtnet_restore(struct virtio_device *vdev)
        if (err)
                return err;
 
+       virtio_device_ready(vdev);
+
        if (netif_running(vi->dev)) {
                for (i = 0; i < vi->curr_queue_pairs; i++)
                        if (!try_fill_recv(&vi->rq[i], GFP_KERNEL))
@@ -1948,10 +1932,6 @@ static int virtnet_restore(struct virtio_device *vdev)
 
        netif_device_attach(vi->dev);
 
-       mutex_lock(&vi->config_lock);
-       vi->config_enable = true;
-       mutex_unlock(&vi->config_lock);
-
        rtnl_lock();
        virtnet_set_queues(vi, vi->curr_queue_pairs);
        rtnl_unlock();
index a134965..6431290 100644 (file)
@@ -406,15 +406,8 @@ static void kvm_extint_handler(struct ext_code ext_code,
 
        switch (param) {
        case VIRTIO_PARAM_CONFIG_CHANGED:
-       {
-               struct virtio_driver *drv;
-               drv = container_of(vq->vdev->dev.driver,
-                                  struct virtio_driver, driver);
-               if (drv->config_changed)
-                       drv->config_changed(vq->vdev);
-
+               virtio_config_changed(vq->vdev);
                break;
-       }
        case VIRTIO_PARAM_DEV_ADD:
                schedule_work(&hotplug_work);
                break;
index d2c0b44..6cbe6ef 100644 (file)
@@ -940,11 +940,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
                vring_interrupt(0, vq);
        }
        if (test_bit(0, &vcdev->indicators2)) {
-               drv = container_of(vcdev->vdev.dev.driver,
-                                  struct virtio_driver, driver);
-
-               if (drv && drv->config_changed)
-                       drv->config_changed(&vcdev->vdev);
+               virtio_config_changed(&vcdev->vdev);
                clear_bit(0, &vcdev->indicators2);
        }
 }
index eee1bc0..b83846f 100644 (file)
@@ -110,6 +110,9 @@ struct virtio_scsi {
        /* CPU hotplug notifier */
        struct notifier_block nb;
 
+       /* Protected by event_vq lock */
+       bool stop_events;
+
        struct virtio_scsi_vq ctrl_vq;
        struct virtio_scsi_vq event_vq;
        struct virtio_scsi_vq req_vqs[];
@@ -303,6 +306,11 @@ static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi)
 {
        int i;
 
+       /* Stop scheduling work before calling cancel_work_sync.  */
+       spin_lock_irq(&vscsi->event_vq.vq_lock);
+       vscsi->stop_events = true;
+       spin_unlock_irq(&vscsi->event_vq.vq_lock);
+
        for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++)
                cancel_work_sync(&vscsi->event_list[i].work);
 }
@@ -390,7 +398,8 @@ static void virtscsi_complete_event(struct virtio_scsi *vscsi, void *buf)
 {
        struct virtio_scsi_event_node *event_node = buf;
 
-       schedule_work(&event_node->work);
+       if (!vscsi->stop_events)
+               queue_work(system_freezable_wq, &event_node->work);
 }
 
 static void virtscsi_event_done(struct virtqueue *vq)
@@ -851,13 +860,6 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq,
        virtscsi_vq->vq = vq;
 }
 
-static void virtscsi_scan(struct virtio_device *vdev)
-{
-       struct Scsi_Host *shost = (struct Scsi_Host *)vdev->priv;
-
-       scsi_scan_host(shost);
-}
-
 static void virtscsi_remove_vqs(struct virtio_device *vdev)
 {
        struct Scsi_Host *sh = virtio_scsi_host(vdev);
@@ -916,9 +918,6 @@ static int virtscsi_init(struct virtio_device *vdev,
        virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
        virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
 
-       if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
-               virtscsi_kick_event_all(vscsi);
-
        err = 0;
 
 out:
@@ -997,10 +996,13 @@ static int virtscsi_probe(struct virtio_device *vdev)
        err = scsi_add_host(shost, &vdev->dev);
        if (err)
                goto scsi_add_host_failed;
-       /*
-        * scsi_scan_host() happens in virtscsi_scan() via virtio_driver->scan()
-        * after VIRTIO_CONFIG_S_DRIVER_OK has been set..
-        */
+
+       virtio_device_ready(vdev);
+
+       if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
+               virtscsi_kick_event_all(vscsi);
+
+       scsi_scan_host(shost);
        return 0;
 
 scsi_add_host_failed:
@@ -1048,8 +1050,15 @@ static int virtscsi_restore(struct virtio_device *vdev)
                return err;
 
        err = register_hotcpu_notifier(&vscsi->nb);
-       if (err)
+       if (err) {
                vdev->config->del_vqs(vdev);
+               return err;
+       }
+
+       virtio_device_ready(vdev);
+
+       if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG))
+               virtscsi_kick_event_all(vscsi);
 
        return err;
 }
@@ -1073,7 +1082,6 @@ static struct virtio_driver virtio_scsi_driver = {
        .driver.owner = THIS_MODULE,
        .id_table = id_table,
        .probe = virtscsi_probe,
-       .scan = virtscsi_scan,
 #ifdef CONFIG_PM_SLEEP
        .freeze = virtscsi_freeze,
        .restore = virtscsi_restore,
index fed0ce1..df598dd 100644 (file)
@@ -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,6 +284,64 @@ void unregister_virtio_device(struct virtio_device *dev)
 }
 EXPORT_SYMBOL_GPL(unregister_virtio_device);
 
+#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)
+               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);
+
+       virtio_config_enable(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(virtio_device_restore);
+#endif
+
 static int virtio_init(void)
 {
        if (bus_register(&virtio_bus) != 0)
index f893148..c9703d4 100644 (file)
@@ -504,6 +504,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;
index c600ccf..ef9a165 100644 (file)
@@ -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;
        }
 
index 3d1463c..d34ebfa 100644 (file)
@@ -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;
 };
@@ -211,12 +208,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;
 }
 
@@ -768,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);
@@ -788,27 +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;
        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);
-       vp_finalize_features(&vp_dev->vdev);
-
-       if (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;
+       return virtio_device_restore(&vp_dev->vdev);
 }
 
 static const struct dev_pm_ops virtio_pci_pm_ops = {
index b46671e..65261a7 100644 (file)
@@ -78,6 +78,10 @@ bool virtqueue_is_broken(struct virtqueue *vq);
 /**
  * virtio_device - representation of a device using virtio
  * @index: unique position on the virtio bus
+ * @failed: saved value for CONFIG_S_FAILED bit (for restore)
+ * @config_enabled: configuration change reporting enabled
+ * @config_change_pending: configuration change reported while disabled
+ * @config_lock: protects configuration change reporting
  * @dev: underlying device.
  * @id: the device type identification (used to match it with a driver).
  * @config: the configuration ops for this device.
@@ -88,6 +92,10 @@ bool virtqueue_is_broken(struct virtqueue *vq);
  */
 struct virtio_device {
        int index;
+       bool failed;
+       bool config_enabled;
+       bool config_change_pending;
+       spinlock_t config_lock;
        struct device dev;
        struct virtio_device_id id;
        const struct virtio_config_ops *config;
@@ -108,6 +116,12 @@ void unregister_virtio_device(struct virtio_device *dev);
 
 void virtio_break_device(struct virtio_device *dev);
 
+void virtio_config_changed(struct virtio_device *dev);
+#ifdef CONFIG_PM_SLEEP
+int virtio_device_freeze(struct virtio_device *dev);
+int virtio_device_restore(struct virtio_device *dev);
+#endif
+
 /**
  * virtio_driver - operations for a virtio I/O driver
  * @driver: underlying device driver (populate name and owner).
index e8f8f71..7f4ef66 100644 (file)
@@ -109,6 +109,23 @@ struct virtqueue *virtio_find_single_vq(struct virtio_device *vdev,
        return vq;
 }
 
+/**
+ * virtio_device_ready - enable vq use in probe function
+ * @vdev: the device
+ *
+ * Driver must call this to use vqs in the probe function.
+ *
+ * Note: vqs are enabled automatically after probe returns.
+ */
+static inline
+void virtio_device_ready(struct virtio_device *dev)
+{
+       unsigned status = dev->config->get_status(dev);
+
+       BUG_ON(status & VIRTIO_CONFIG_S_DRIVER_OK);
+       dev->config->set_status(dev, status | VIRTIO_CONFIG_S_DRIVER_OK);
+}
+
 static inline
 const char *virtio_bus_name(struct virtio_device *vdev)
 {
index 6940d8f..daa749c 100644 (file)
@@ -575,6 +575,8 @@ static int p9_virtio_probe(struct virtio_device *vdev)
        /* Ceiling limit to avoid denial of service attacks */
        chan->p9_max_pages = nr_free_buffer_pages()/4;
 
+       virtio_device_ready(vdev);
+
        mutex_lock(&virtio_9p_lock);
        list_add_tail(&chan->chan_list, &virtio_chan_list);
        mutex_unlock(&virtio_9p_lock);