Merge branch 'fix/acer-alc889-fix' into fix/hda
[firefly-linux-kernel-4.4.55.git] / drivers / block / virtio_blk.c
index ba73661fb9f39ff21bf8ed955ac782351aa2233f..c4a60badf252caee7bffff38cc0cf115d0bd81a4 100644 (file)
@@ -250,8 +250,8 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
        if (!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_SCSI))
                return -ENOTTY;
 
-       return scsi_cmd_ioctl(disk->queue, disk, mode, cmd,
-                             (void __user *)data);
+       return scsi_cmd_blk_ioctl(bdev, mode, cmd,
+                                 (void __user *)data);
 }
 
 /* We provide getgeo only to please some old bootloader/partitioning tools */
@@ -362,6 +362,18 @@ static void virtblk_config_changed(struct virtio_device *vdev)
        queue_work(virtblk_wq, &vblk->config_work);
 }
 
+static int init_vq(struct virtio_blk *vblk)
+{
+       int err = 0;
+
+       /* We expect one virtqueue, for output. */
+       vblk->vq = virtio_find_single_vq(vblk->vdev, blk_done, "requests");
+       if (IS_ERR(vblk->vq))
+               err = PTR_ERR(vblk->vq);
+
+       return err;
+}
+
 static int __devinit virtblk_probe(struct virtio_device *vdev)
 {
        struct virtio_blk *vblk;
@@ -405,12 +417,9 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
        INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
        vblk->config_enable = true;
 
-       /* We expect one virtqueue, for output. */
-       vblk->vq = virtio_find_single_vq(vdev, blk_done, "requests");
-       if (IS_ERR(vblk->vq)) {
-               err = PTR_ERR(vblk->vq);
+       err = init_vq(vblk);
+       if (err)
                goto out_free_vblk;
-       }
 
        vblk->pool = mempool_create_kmalloc_pool(1,sizeof(struct virtblk_req));
        if (!vblk->pool) {
@@ -579,6 +588,46 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
        ida_simple_remove(&vd_index_ida, index);
 }
 
+#ifdef CONFIG_PM
+static int virtblk_freeze(struct virtio_device *vdev)
+{
+       struct virtio_blk *vblk = vdev->priv;
+
+       /* 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);
+
+       flush_work(&vblk->config_work);
+
+       spin_lock_irq(vblk->disk->queue->queue_lock);
+       blk_stop_queue(vblk->disk->queue);
+       spin_unlock_irq(vblk->disk->queue->queue_lock);
+       blk_sync_queue(vblk->disk->queue);
+
+       vdev->config->del_vqs(vdev);
+       return 0;
+}
+
+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) {
+               spin_lock_irq(vblk->disk->queue->queue_lock);
+               blk_start_queue(vblk->disk->queue);
+               spin_unlock_irq(vblk->disk->queue->queue_lock);
+       }
+       return ret;
+}
+#endif
+
 static const struct virtio_device_id id_table[] = {
        { VIRTIO_ID_BLOCK, VIRTIO_DEV_ANY_ID },
        { 0 },
@@ -604,6 +653,10 @@ static struct virtio_driver __refdata virtio_blk = {
        .probe                  = virtblk_probe,
        .remove                 = __devexit_p(virtblk_remove),
        .config_changed         = virtblk_config_changed,
+#ifdef CONFIG_PM
+       .freeze                 = virtblk_freeze,
+       .restore                = virtblk_restore,
+#endif
 };
 
 static int __init init(void)