Merge tag 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mst/vhost
[linux-block.git] / drivers / vhost / scsi.c
index 282aac45c690995cbe88151ea12cc565bf84d61d..006ffacf1c56cbecf92ca7cbd2f03ee31bde913f 100644 (file)
@@ -210,6 +210,7 @@ struct vhost_scsi {
 
 struct vhost_scsi_tmf {
        struct vhost_work vwork;
+       struct work_struct flush_work;
        struct vhost_scsi *vhost;
        struct vhost_scsi_virtqueue *svq;
 
@@ -358,14 +359,23 @@ static void vhost_scsi_release_tmf_res(struct vhost_scsi_tmf *tmf)
        vhost_scsi_put_inflight(inflight);
 }
 
+static void vhost_scsi_drop_cmds(struct vhost_scsi_virtqueue *svq)
+{
+       struct vhost_scsi_cmd *cmd, *t;
+       struct llist_node *llnode;
+
+       llnode = llist_del_all(&svq->completion_list);
+       llist_for_each_entry_safe(cmd, t, llnode, tvc_completion_list)
+               vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd);
+}
+
 static void vhost_scsi_release_cmd(struct se_cmd *se_cmd)
 {
        if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) {
                struct vhost_scsi_tmf *tmf = container_of(se_cmd,
                                        struct vhost_scsi_tmf, se_cmd);
-               struct vhost_virtqueue *vq = &tmf->svq->vq;
 
-               vhost_vq_work_queue(vq, &tmf->vwork);
+               schedule_work(&tmf->flush_work);
        } else {
                struct vhost_scsi_cmd *cmd = container_of(se_cmd,
                                        struct vhost_scsi_cmd, tvc_se_cmd);
@@ -373,7 +383,8 @@ static void vhost_scsi_release_cmd(struct se_cmd *se_cmd)
                                        struct vhost_scsi_virtqueue, vq);
 
                llist_add(&cmd->tvc_completion_list, &svq->completion_list);
-               vhost_vq_work_queue(&svq->vq, &svq->completion_work);
+               if (!vhost_vq_work_queue(&svq->vq, &svq->completion_work))
+                       vhost_scsi_drop_cmds(svq);
        }
 }
 
@@ -497,10 +508,8 @@ again:
                vq_err(vq, "Faulted on vhost_scsi_send_event\n");
 }
 
-static void vhost_scsi_evt_work(struct vhost_work *work)
+static void vhost_scsi_complete_events(struct vhost_scsi *vs, bool drop)
 {
-       struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
-                                       vs_event_work);
        struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
        struct vhost_scsi_evt *evt, *t;
        struct llist_node *llnode;
@@ -508,12 +517,20 @@ static void vhost_scsi_evt_work(struct vhost_work *work)
        mutex_lock(&vq->mutex);
        llnode = llist_del_all(&vs->vs_event_list);
        llist_for_each_entry_safe(evt, t, llnode, list) {
-               vhost_scsi_do_evt_work(vs, evt);
+               if (!drop)
+                       vhost_scsi_do_evt_work(vs, evt);
                vhost_scsi_free_evt(vs, evt);
        }
        mutex_unlock(&vq->mutex);
 }
 
+static void vhost_scsi_evt_work(struct vhost_work *work)
+{
+       struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
+                                            vs_event_work);
+       vhost_scsi_complete_events(vs, false);
+}
+
 static int vhost_scsi_copy_sgl_to_iov(struct vhost_scsi_cmd *cmd)
 {
        struct iov_iter *iter = &cmd->saved_iter;
@@ -1270,33 +1287,32 @@ static void vhost_scsi_tmf_resp_work(struct vhost_work *work)
 {
        struct vhost_scsi_tmf *tmf = container_of(work, struct vhost_scsi_tmf,
                                                  vwork);
-       struct vhost_virtqueue *ctl_vq, *vq;
-       int resp_code, i;
-
-       if (tmf->scsi_resp == TMR_FUNCTION_COMPLETE) {
-               /*
-                * Flush IO vqs that don't share a worker with the ctl to make
-                * sure they have sent their responses before us.
-                */
-               ctl_vq = &tmf->vhost->vqs[VHOST_SCSI_VQ_CTL].vq;
-               for (i = VHOST_SCSI_VQ_IO; i < tmf->vhost->dev.nvqs; i++) {
-                       vq = &tmf->vhost->vqs[i].vq;
-
-                       if (vhost_vq_is_setup(vq) &&
-                           vq->worker != ctl_vq->worker)
-                               vhost_vq_flush(vq);
-               }
+       int resp_code;
 
+       if (tmf->scsi_resp == TMR_FUNCTION_COMPLETE)
                resp_code = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
-       } else {
+       else
                resp_code = VIRTIO_SCSI_S_FUNCTION_REJECTED;
-       }
 
        vhost_scsi_send_tmf_resp(tmf->vhost, &tmf->svq->vq, tmf->in_iovs,
                                 tmf->vq_desc, &tmf->resp_iov, resp_code);
        vhost_scsi_release_tmf_res(tmf);
 }
 
+static void vhost_scsi_tmf_flush_work(struct work_struct *work)
+{
+       struct vhost_scsi_tmf *tmf = container_of(work, struct vhost_scsi_tmf,
+                                                flush_work);
+       struct vhost_virtqueue *vq = &tmf->svq->vq;
+       /*
+        * Make sure we have sent responses for other commands before we
+        * send our response.
+        */
+       vhost_dev_flush(vq->dev);
+       if (!vhost_vq_work_queue(vq, &tmf->vwork))
+               vhost_scsi_release_tmf_res(tmf);
+}
+
 static void
 vhost_scsi_handle_tmf(struct vhost_scsi *vs, struct vhost_scsi_tpg *tpg,
                      struct vhost_virtqueue *vq,
@@ -1320,6 +1336,7 @@ vhost_scsi_handle_tmf(struct vhost_scsi *vs, struct vhost_scsi_tpg *tpg,
        if (!tmf)
                goto send_reject;
 
+       INIT_WORK(&tmf->flush_work, vhost_scsi_tmf_flush_work);
        vhost_work_init(&tmf->vwork, vhost_scsi_tmf_resp_work);
        tmf->vhost = vs;
        tmf->svq = svq;
@@ -1509,7 +1526,8 @@ vhost_scsi_send_evt(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
        }
 
        llist_add(&evt->list, &vs->vs_event_list);
-       vhost_vq_work_queue(vq, &vs->vs_event_work);
+       if (!vhost_vq_work_queue(vq, &vs->vs_event_work))
+               vhost_scsi_complete_events(vs, true);
 }
 
 static void vhost_scsi_evt_handle_kick(struct vhost_work *work)