On Thu, Jul 26, 2012 at 4:43 PM, Nicholas A. Bellinger
<n...@linux-iscsi.org>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 majord...@vger.kernel.org
> 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