Merge tag 'asoc-v3.18-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
[cascardo/linux.git] / drivers / scsi / virtio_scsi.c
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,