qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC-v5] tcm_vhost: Initial merge for vhost level targe


From: Eric Northup
Subject: Re: [Qemu-devel] [RFC-v5] tcm_vhost: Initial merge for vhost level target fabric driver
Date: Tue, 31 Jul 2012 13:52:12 -0700

On Thu, Jul 26, 2012 at 4:43 PM, Nicholas A. Bellinger <address@hidden> wrote:
[...]
+static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
+{
+       struct vhost_virtqueue *vq = &vs->vqs[2];
+       struct virtio_scsi_cmd_req v_req;
+       struct tcm_vhost_tpg *tv_tpg;
+       struct tcm_vhost_cmd *tv_cmd;
+       u32 exp_data_len, data_first, data_num, data_direction;
+       unsigned out, in, i;
+       int head, ret;
+
+       /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */
+       tv_tpg = vs->vs_tpg;
+       if (unlikely(!tv_tpg)) {
+               pr_err("%s endpoint not set\n", __func__);
+               return;
+       }
+
+       mutex_lock(&vq->mutex);
+       vhost_disable_notify(&vs->dev, vq);
+
+       for (;;) {
+               head = vhost_get_vq_desc(&vs->dev, vq, vq->iov,
+                                       ARRAY_SIZE(vq->iov), &out, &in,
+                                       NULL, NULL);
+               pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n",
+                                       head, out, in);
+               /* On error, stop handling until the next kick. */
+               if (unlikely(head < 0))
+                       break;
+               /* Nothing new?  Wait for eventfd to tell us they refilled. */
+               if (head == vq->num) {
+                       if (unlikely(vhost_enable_notify(&vs->dev, vq))) {
+                               vhost_disable_notify(&vs->dev, vq);
+                               continue;
+                       }
+                       break;
+               }
+
+/* FIXME: BIDI operation */
+               if (out == 1 && in == 1) {

It seems to me like this is not the way that virtio devices are supposed to behave - if a guest splits a virtio_scsi_cmd_req or _resp across a page boundary, then this code won't work.

Quoting the 'Message Framing' part of the virtio spec:

"In particular, no implementation should use the descriptor boundaries to determine the size of any header in a request. "


+                       data_direction = DMA_NONE;
+                       data_first = 0;
+                       data_num = 0;
+               } else if (out == 1 && in > 1) {
+                       data_direction = DMA_FROM_DEVICE;
+                       data_first = out + 1;
+                       data_num = in - 1;
+               } else if (out > 1 && in == 1) {
+                       data_direction = DMA_TO_DEVICE;
+                       data_first = 1;
+                       data_num = out - 1;
+               } else {
+                       vq_err(vq, "Invalid buffer layout out: %u in: %u\n",
+                                       out, in);
+                       break;
+               }
+
+               /*
+                * Check for a sane resp buffer so we can report errors to
+                * the guest.
+                */
+               if (unlikely(vq->iov[out].iov_len !=
+                                       sizeof(struct virtio_scsi_cmd_resp))) {
+                       vq_err(vq, "Expecting virtio_scsi_cmd_resp, got %zu"
+                               " bytes\n", vq->iov[out].iov_len);
+                       break;
+               }
+
+               if (unlikely(vq->iov[0].iov_len != sizeof(v_req))) {
+                       vq_err(vq, "Expecting virtio_scsi_cmd_req, got %zu"
+                               " bytes\n", vq->iov[0].iov_len);
+                       break;
+               }
+               pr_debug("Calling __copy_from_user: vq->iov[0].iov_base: %p,"
+                       " len: %zu\n", vq->iov[0].iov_base, sizeof(v_req));
+               ret = __copy_from_user(&v_req, vq->iov[0].iov_base,
+                               sizeof(v_req));
+               if (unlikely(ret)) {
+                       vq_err(vq, "Faulted on virtio_scsi_cmd_req\n");
+                       break;
+               }
+
+               exp_data_len = 0;
+               for (i = 0; i < data_num; i++)
+                       exp_data_len += vq->iov[data_first + i].iov_len;
+
+               tv_cmd = vhost_scsi_allocate_cmd(tv_tpg, &v_req,
+                                       exp_data_len, data_direction);
+               if (IS_ERR(tv_cmd)) {
+                       vq_err(vq, "vhost_scsi_allocate_cmd failed %ld\n",
+                                       PTR_ERR(tv_cmd));
+                       break;
+               }
+               pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction"
+                       ": %d\n", tv_cmd, exp_data_len, data_direction);
+
+               tv_cmd->tvc_vhost = vs;
+
+               if (unlikely(vq->iov[out].iov_len !=
+                               sizeof(struct virtio_scsi_cmd_resp))) {
+                       vq_err(vq, "Expecting virtio_scsi_cmd_resp, got %zu"
+                               " bytes, out: %d, in: %d\n",
+                               vq->iov[out].iov_len, out, in);
+                       break;
+               }
+
+               tv_cmd->tvc_resp = vq->iov[out].iov_base;
+
+               /*
+                * Copy in the recieved CDB descriptor into tv_cmd->tvc_cdb
+                * that will be used by tcm_vhost_new_cmd_map() and down into
+                * target_setup_cmd_from_cdb()
+                */
+               memcpy(tv_cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
+               /*
+                * Check that the recieved CDB size does not exceeded our
+                * hardcoded max for tcm_vhost
+                */
+               /* TODO what if cdb was too small for varlen cdb header? */
+               if (unlikely(scsi_command_size(tv_cmd->tvc_cdb) >
+                                       TCM_VHOST_MAX_CDB_SIZE)) {
+                       vq_err(vq, "Received SCSI CDB with command_size: %d that"
+                               " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
+                               scsi_command_size(tv_cmd->tvc_cdb),
+                               TCM_VHOST_MAX_CDB_SIZE);
+                       break; /* TODO */
+               }
+               tv_cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+
+               pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
+                       tv_cmd->tvc_cdb[0], tv_cmd->tvc_lun);
+
+               if (data_direction != DMA_NONE) {
+                       ret = vhost_scsi_map_iov_to_sgl(tv_cmd,
+                                       &vq->iov[data_first], data_num,
+                                       data_direction == DMA_TO_DEVICE);
+                       if (unlikely(ret)) {
+                               vq_err(vq, "Failed to map iov to sgl\n");
+                               break; /* TODO */
+                       }
+               }
+
+               /*
+                * Save the descriptor from vhost_get_vq_desc() to be used to
+                * complete the virtio-scsi request in TCM callback context via
+                * tcm_vhost_queue_data_in() and tcm_vhost_queue_status()
+                */
+               tv_cmd->tvc_vq_desc = head;
+               /*
+                * Dispatch tv_cmd descriptor for cmwq execution in process
+                * context provided by tcm_vhost_workqueue.  This also ensures
+                * tv_cmd is executed on the same kworker CPU as this vhost
+                * thread to gain positive L2 cache locality effects..
+                */
+               INIT_WORK(&tv_cmd->work, tcm_vhost_submission_work);
+               queue_work(tcm_vhost_workqueue, &tv_cmd->work);
+       }
+
+       mutex_unlock(&vq->mutex);
+}
[...]
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to address@hidden
More majordomo info at  http://vger.kernel.org/majordomo-info.html



--
Typing one-handed, please don't mistake brevity for rudeness.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]