ipr: add support for async scanning to speed up boot
[cascardo/linux.git] / drivers / scsi / virtio_scsi.c
index eee1bc0..22e7012 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)
@@ -552,6 +561,15 @@ static int virtscsi_queuecommand_single(struct Scsi_Host *sh,
        return virtscsi_queuecommand(vscsi, &vscsi->req_vqs[0], sc);
 }
 
+static struct virtio_scsi_vq *virtscsi_pick_vq_mq(struct virtio_scsi *vscsi,
+                                                 struct scsi_cmnd *sc)
+{
+       u32 tag = blk_mq_unique_tag(sc->request);
+       u16 hwq = blk_mq_unique_tag_to_hwq(tag);
+
+       return &vscsi->req_vqs[hwq];
+}
+
 static struct virtio_scsi_vq *virtscsi_pick_vq(struct virtio_scsi *vscsi,
                                               struct virtio_scsi_target_state *tgt)
 {
@@ -595,7 +613,12 @@ static int virtscsi_queuecommand_multi(struct Scsi_Host *sh,
        struct virtio_scsi *vscsi = shost_priv(sh);
        struct virtio_scsi_target_state *tgt =
                                scsi_target(sc->device)->hostdata;
-       struct virtio_scsi_vq *req_vq = virtscsi_pick_vq(vscsi, tgt);
+       struct virtio_scsi_vq *req_vq;
+
+       if (shost_use_blk_mq(sh))
+               req_vq = virtscsi_pick_vq_mq(vscsi, sc);
+       else
+               req_vq = virtscsi_pick_vq(vscsi, tgt);
 
        return virtscsi_queuecommand(vscsi, req_vq, sc);
 }
@@ -659,30 +682,13 @@ static int virtscsi_device_reset(struct scsi_cmnd *sc)
  * virtscsi_change_queue_depth() - Change a virtscsi target's queue depth
  * @sdev:      Virtscsi target whose queue depth to change
  * @qdepth:    New queue depth
- * @reason:    Reason for the queue depth change.
  */
-static int virtscsi_change_queue_depth(struct scsi_device *sdev,
-                                      int qdepth,
-                                      int reason)
+static int virtscsi_change_queue_depth(struct scsi_device *sdev, int qdepth)
 {
        struct Scsi_Host *shost = sdev->host;
        int max_depth = shost->cmd_per_lun;
 
-       switch (reason) {
-       case SCSI_QDEPTH_QFULL: /* Drop qdepth in response to BUSY state */
-               scsi_track_queue_full(sdev, qdepth);
-               break;
-       case SCSI_QDEPTH_RAMP_UP: /* Raise qdepth after BUSY state resolved */
-       case SCSI_QDEPTH_DEFAULT: /* Manual change via sysfs */
-               scsi_adjust_queue_depth(sdev,
-                                       scsi_get_tag_type(sdev),
-                                       min(max_depth, qdepth));
-               break;
-       default:
-               return -EOPNOTSUPP;
-       }
-
-       return sdev->queue_depth;
+       return scsi_change_queue_depth(sdev, min(max_depth, qdepth));
 }
 
 static int virtscsi_abort(struct scsi_cmnd *sc)
@@ -749,6 +755,7 @@ static struct scsi_host_template virtscsi_host_template_single = {
        .use_clustering = ENABLE_CLUSTERING,
        .target_alloc = virtscsi_target_alloc,
        .target_destroy = virtscsi_target_destroy,
+       .track_queue_depth = 1,
 };
 
 static struct scsi_host_template virtscsi_host_template_multi = {
@@ -767,6 +774,7 @@ static struct scsi_host_template virtscsi_host_template_multi = {
        .use_clustering = ENABLE_CLUSTERING,
        .target_alloc = virtscsi_target_alloc,
        .target_destroy = virtscsi_target_destroy,
+       .track_queue_depth = 1,
 };
 
 #define virtscsi_config_get(vdev, fld) \
@@ -851,13 +859,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 +917,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:
@@ -984,6 +982,7 @@ static int virtscsi_probe(struct virtio_device *vdev)
        shost->max_id = num_targets;
        shost->max_channel = 0;
        shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
+       shost->nr_hw_queues = num_queues;
 
        if (virtio_has_feature(vdev, VIRTIO_SCSI_F_T10_PI)) {
                host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION |
@@ -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,