The vhost-scsi I/O queue uses vhost_scsi_cmd. Allocate the log buffer
during vhost_scsi_cmd allocation or when VHOST_F_LOG_ALL is set. Free the
log buffer when vhost_scsi_cmd is reclaimed or when VHOST_F_LOG_ALL is
removed.

Fail vhost_scsi_set_endpoint or vhost_scsi_set_features() on allocation
failure.

The cached log buffer will be uses in upcoming patches to log write
descriptors for the I/O queue. The core idea is to cache the log in the
per-command log buffer in the submission path, and use them to log write
descriptors in the completion path.

As a reminder, currently QEMU's vhost-scsi VHOST_SET_FEATURES handler
doesn't process the failure gracefully. Instead, it crashes immediately on
failure from VHOST_SET_FEATURES.

Suggested-by: Joao Martins <joao.m.mart...@oracle.com>
Signed-off-by: Dongli Zhang <dongli.zh...@oracle.com>
---
Changed since v1:
  - Don't allocate log buffer during initialization. Allocate during
    VHOST_SET_FEATURES or VHOST_SCSI_SET_ENDPOINT.

 drivers/vhost/scsi.c | 126 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 3875967dee36..1b7211a55562 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -133,6 +133,11 @@ struct vhost_scsi_cmd {
        struct se_cmd tvc_se_cmd;
        /* Sense buffer that will be mapped into outgoing status */
        unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER];
+       /*
+        * Dirty write descriptors of this command.
+        */
+       struct vhost_log *tvc_log;
+       unsigned int tvc_log_num;
        /* Completed commands list, serviced from vhost worker thread */
        struct llist_node tvc_completion_list;
        /* Used to track inflight cmd */
@@ -676,6 +681,7 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, u64 scsi_tag)
                                        struct vhost_scsi_virtqueue, vq);
        struct vhost_scsi_cmd *cmd;
        struct scatterlist *sgl, *prot_sgl;
+       struct vhost_log *log;
        int tag;
 
        tag = sbitmap_get(&svq->scsi_tags);
@@ -687,9 +693,11 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, u64 
scsi_tag)
        cmd = &svq->scsi_cmds[tag];
        sgl = cmd->sgl;
        prot_sgl = cmd->prot_sgl;
+       log = cmd->tvc_log;
        memset(cmd, 0, sizeof(*cmd));
        cmd->sgl = sgl;
        cmd->prot_sgl = prot_sgl;
+       cmd->tvc_log = log;
        cmd->tvc_se_cmd.map_tag = tag;
        cmd->inflight = vhost_scsi_get_inflight(vq);
 
@@ -1760,6 +1768,55 @@ static void vhost_scsi_flush(struct vhost_scsi *vs)
                wait_for_completion(&vs->old_inflight[i]->comp);
 }
 
+static void vhost_scsi_destroy_vq_log(struct vhost_virtqueue *vq)
+{
+       struct vhost_scsi_virtqueue *svq = container_of(vq,
+                                       struct vhost_scsi_virtqueue, vq);
+       struct vhost_scsi_cmd *tv_cmd;
+       unsigned int i;
+
+       if (!svq->scsi_cmds)
+               return;
+
+       for (i = 0; i < svq->max_cmds; i++) {
+               tv_cmd = &svq->scsi_cmds[i];
+               kfree(tv_cmd->tvc_log);
+               tv_cmd->tvc_log = NULL;
+               tv_cmd->tvc_log_num = 0;
+       }
+}
+
+static int vhost_scsi_setup_vq_log(struct vhost_virtqueue *vq)
+{
+       struct vhost_scsi_virtqueue *svq = container_of(vq,
+                                       struct vhost_scsi_virtqueue, vq);
+       struct vhost_scsi_cmd *tv_cmd;
+       unsigned int i;
+
+       if (!svq->scsi_cmds)
+               return 0;
+
+       for (i = 0; i < svq->max_cmds; i++) {
+               tv_cmd = &svq->scsi_cmds[i];
+               WARN_ON_ONCE(unlikely(tv_cmd->tvc_log ||
+                                     tv_cmd->tvc_log_num));
+               tv_cmd->tvc_log_num = 0;
+               tv_cmd->tvc_log = kcalloc(vq->dev->iov_limit,
+                                         sizeof(struct vhost_log),
+                                         GFP_KERNEL);
+               if (!tv_cmd->tvc_log) {
+                       pr_err("Unable to allocate tv_cmd->tvc_log\n");
+                       goto err;
+               }
+       }
+
+       return 0;
+
+err:
+       vhost_scsi_destroy_vq_log(vq);
+       return -ENOMEM;
+}
+
 static void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq)
 {
        struct vhost_scsi_virtqueue *svq = container_of(vq,
@@ -1779,6 +1836,7 @@ static void vhost_scsi_destroy_vq_cmds(struct 
vhost_virtqueue *vq)
 
        sbitmap_free(&svq->scsi_tags);
        kfree(svq->upages);
+       vhost_scsi_destroy_vq_log(vq);
        kfree(svq->scsi_cmds);
        svq->scsi_cmds = NULL;
 }
@@ -1834,6 +1892,11 @@ static int vhost_scsi_setup_vq_cmds(struct 
vhost_virtqueue *vq, int max_cmds)
                        }
                }
        }
+
+       if (vhost_has_feature(vq, VHOST_F_LOG_ALL) &&
+           vhost_scsi_setup_vq_log(vq))
+               goto out;
+
        return 0;
 out:
        vhost_scsi_destroy_vq_cmds(vq);
@@ -2088,6 +2151,8 @@ vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
 static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
 {
        struct vhost_virtqueue *vq;
+       bool is_log, was_log;
+       int ret;
        int i;
 
        if (features & ~VHOST_SCSI_FEATURES)
@@ -2100,14 +2165,75 @@ static int vhost_scsi_set_features(struct vhost_scsi 
*vs, u64 features)
                return -EFAULT;
        }
 
+       if (!vs->dev.nvqs)
+               goto out;
+
+       is_log = features & (1 << VHOST_F_LOG_ALL);
+       /*
+        * All VQs should have same feature.
+        */
+       was_log = vhost_has_feature(&vs->vqs[0].vq, VHOST_F_LOG_ALL);
+
+       /*
+        * If VHOST_F_LOG_ALL is going to be added, allocate tvc_log before
+        * vq->acked_features is committed.
+        * Return -ENOMEM on allocation failure.
+        */
+       if (is_log && !was_log) {
+               for (i = VHOST_SCSI_VQ_IO; i < vs->dev.nvqs; i++) {
+                       if (!vs->vqs[i].scsi_cmds)
+                               continue;
+
+                       vq = &vs->vqs[i].vq;
+
+                       mutex_lock(&vq->mutex);
+                       ret = vhost_scsi_setup_vq_log(vq);
+                       mutex_unlock(&vq->mutex);
+
+                       if (ret)
+                               goto destroy_cmd_log;
+               }
+       }
+
        for (i = 0; i < vs->dev.nvqs; i++) {
                vq = &vs->vqs[i].vq;
                mutex_lock(&vq->mutex);
                vq->acked_features = features;
                mutex_unlock(&vq->mutex);
        }
+
+       /*
+        * If VHOST_F_LOG_ALL is removed, free tvc_log after
+        * vq->acked_features is committed.
+        */
+       if (!is_log && was_log) {
+               for (i = VHOST_SCSI_VQ_IO; i < vs->dev.nvqs; i++) {
+                       if (!vs->vqs[i].scsi_cmds)
+                               continue;
+
+                       vq = &vs->vqs[i].vq;
+                       mutex_lock(&vq->mutex);
+                       vhost_scsi_destroy_vq_log(vq);
+                       mutex_unlock(&vq->mutex);
+               }
+       }
+
+out:
        mutex_unlock(&vs->dev.mutex);
        return 0;
+
+destroy_cmd_log:
+       for (i--; i >= VHOST_SCSI_VQ_IO; i--) {
+               if (!vs->vqs[i].scsi_cmds)
+                       continue;
+
+               vq = &vs->vqs[i].vq;
+               mutex_lock(&vq->mutex);
+               vhost_scsi_destroy_vq_log(vq);
+               mutex_unlock(&vq->mutex);
+       }
+       mutex_unlock(&vs->dev.mutex);
+       return -ENOMEM;
 }
 
 static int vhost_scsi_open(struct inode *inode, struct file *f)
-- 
2.39.3


Reply via email to