From: "Michael S. Tsirkin" <mst@redhat.com>
To: "Nicholas A. Bellinger" <nab@daterainc.com>
Cc: target-devel <target-devel@vger.kernel.org>,
linux-scsi <linux-scsi@vger.kernel.org>,
kvm-devel <kvm@vger.kernel.org>,
Paolo Bonzini <pbonzini@redhat.com>,
Al Viro <viro@ZenIV.linux.org.uk>, Christoph Hellwig <hch@lst.de>,
Nicholas Bellinger <nab@linux-iscsi.org>
Subject: Re: [PATCH-v3 5/9] vhost/scsi: Add ANY_LAYOUT vhost_virtqueue callback
Date: Tue, 3 Feb 2015 12:14:36 +0200 [thread overview]
Message-ID: <20150203101436.GO2830@redhat.com> (raw)
In-Reply-To: <1422945003-24538-6-git-send-email-nab@daterainc.com>
On Tue, Feb 03, 2015 at 06:29:59AM +0000, Nicholas A. Bellinger wrote:
> From: Nicholas Bellinger <nab@linux-iscsi.org>
>
> This patch adds ANY_LAYOUT support with a new vqs[].vq.handle_kick()
> callback in vhost_scsi_handle_vqal().
>
> It calculates data_direction + exp_data_len for the new tcm_vhost_cmd
> descriptor by walking both outgoing + incoming iovecs using iov_iter,
> assuming the layout of outgoing request header + T10_PI + Data payload
> comes first.
>
> It also uses copy_from_iter() to copy leading virtio-scsi request header
> that may or may not include SCSI CDB, that returns a re-calculated iovec
> to start of T10_PI or Data SGL memory.
>
> v2 changes:
> - Fix up vhost_scsi_handle_vqal comments
> - Minor vhost_scsi_handle_vqal simplifications
> - Add missing minimum virtio-scsi response buffer size check
> - Fix pi_bytes* error message typo
> - Convert to use vhost_skip_iovec_bytes() common code
> - Add max_niov sanity checks vs. out + in offset into vq
>
> v3 changes:
> - Convert to copy_from_iter usage
> - Update vhost_scsi_handle_vqal comments for iov_iter usage
> - Convert prot_bytes offset to use iov_iter_advance
> - Drop max_niov usage in vhost_scsi_handle_vqal
> - Drop vhost_skip_iovec_bytes in favour of iov_iter
>
> Cc: Michael S. Tsirkin <mst@redhat.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
> ---
> drivers/vhost/scsi.c | 260 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 260 insertions(+)
>
> diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
> index 7dfff15..e1fe003 100644
> --- a/drivers/vhost/scsi.c
> +++ b/drivers/vhost/scsi.c
> @@ -1062,6 +1062,266 @@ vhost_scsi_send_bad_target(struct vhost_scsi *vs,
> }
>
> static void
> +vhost_scsi_handle_vqal(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
> +{
> + struct tcm_vhost_tpg **vs_tpg, *tpg;
> + struct virtio_scsi_cmd_req v_req;
> + struct virtio_scsi_cmd_req_pi v_req_pi;
> + struct tcm_vhost_cmd *cmd;
> + struct iov_iter out_iter, in_iter, prot_iter, data_iter;
> + u64 tag;
> + u32 exp_data_len, data_direction;
> + unsigned out, in, i;
> + int head, ret, prot_bytes;
> + size_t req_size, rsp_size = sizeof(struct virtio_scsi_cmd_resp);
> + size_t out_size, in_size;
> + u16 lun;
> + u8 *target, *lunp, task_attr;
> + bool t10_pi = vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI);
> + void *req, *cdb;
> +
> + mutex_lock(&vq->mutex);
> + /*
> + * We can handle the vq only after the endpoint is setup by calling the
> + * VHOST_SCSI_SET_ENDPOINT ioctl.
> + */
> + vs_tpg = vq->private_data;
> + if (!vs_tpg)
> + goto out;
> +
> + vhost_disable_notify(&vs->dev, vq);
> +
> + for (;;) {
> + head = vhost_get_vq_desc(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;
> + }
> + /*
> + * Check for a sane response buffer so we can report early
> + * errors back to the guest.
> + */
> + if (unlikely(vq->iov[out].iov_len < rsp_size)) {
> + vq_err(vq, "Expecting at least virtio_scsi_cmd_resp"
> + " size, got %zu bytes\n", vq->iov[out].iov_len);
> + break;
> + }
> + /*
> + * Setup pointers and values based upon different virtio-scsi
> + * request header if T10_PI is enabled in KVM guest.
> + */
> + if (t10_pi) {
> + req = &v_req_pi;
> + req_size = sizeof(v_req_pi);
> + lunp = &v_req_pi.lun[0];
> + target = &v_req_pi.lun[1];
> + } else {
> + req = &v_req;
> + req_size = sizeof(v_req);
> + lunp = &v_req.lun[0];
> + target = &v_req.lun[1];
> + }
> + /*
> + * Determine data_direction for ANY_LAYOUT
It's not just for ANY_LAYOUT though, is it?
After patchset is fully applied this will run for
all guests?
> by calculating the
> + * total outgoing iovec sizes / incoming iovec sizes vs.
> + * virtio-scsi request / response headers respectively.
> + *
> + * FIXME: Not correct for BIDI operation
> + */
> + out_size = in_size = 0;
> + for (i = 0; i < out; i++)
> + out_size += vq->iov[i].iov_len;
> + for (i = out; i < out + in; i++)
> + in_size += vq->iov[i].iov_len;
Why not use iov_length?
> + /*
> + * Any associated T10_PI bytes for the outgoing / incoming
> + * payloads are included in calculation of exp_data_len here.
> + */
> + if (out_size > req_size) {
> + data_direction = DMA_TO_DEVICE;
> + exp_data_len = out_size - req_size;
> + } else if (in_size > rsp_size) {
> + data_direction = DMA_FROM_DEVICE;
> + exp_data_len = in_size - rsp_size;
> + } else {
> + data_direction = DMA_NONE;
> + exp_data_len = 0;
> + }
We must validate this doesn't cause exp_data_len to be negative.
> + /*
> + * Copy over the virtio-scsi request header, which when
> + * ANY_LAYOUT is enabled may span multiple iovecs, or a
> + * single iovec may contain both the header + outgoing
> + * WRITE payloads.
> + *
> + * copy_from_iter() is modifying the iovecs as copies over
> + * req_size bytes into req, so the returned out_iter.iov[0]
> + * will contain the correct start + offset of the outgoing
> + * WRITE payload, if DMA_TO_DEVICE is set.
> + */
> + iov_iter_init(&out_iter, READ, &vq->iov[0], out,
I'd prefer just using vq->iov here, then we know that
any iov[number] user is left-over that needs to be converted
to use iterators.
> + (data_direction == DMA_TO_DEVICE) ?
> + req_size + exp_data_len : req_size);
Hmm does not look safe: iovecs are pre-validated with
in_size/out_size, not with req_size.
The following should be equivalent, should it not?
iov_iter_init(&out_iter, READ, &vq->iov[0], out,
out_size);
> +
> + ret = copy_from_iter(req, req_size, &out_iter);
> + if (unlikely(ret != req_size)) {
> + vq_err(vq, "Faulted on copy_from_iter\n");
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + /* virtio-scsi spec requires byte 0 of the lun to be 1 */
> + if (unlikely(*lunp != 1)) {
> + vq_err(vq, "Illegal virtio-scsi lun: %u\n", *lunp);
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> +
> + tpg = ACCESS_ONCE(vs_tpg[*target]);
> + if (unlikely(!tpg)) {
> + /* Target does not exist, fail the request */
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + /*
> + * Determine start of T10_PI or data payload iovec in ANY_LAYOUT
> + * mode based upon data_direction.
Same comments as above.
> + *
> + * For DMA_TO_DEVICE, this is iov_out from copy_from_iter()
> + * with the already recalculated iov_base + iov_len.
> + *
> + * For DMA_FROM_DEVICE, the iovec will be just past the end
> + * of the virtio-scsi response header in either the same
> + * or immediately following iovec.
> + */
> + prot_bytes = 0;
> +
> + if (data_direction == DMA_TO_DEVICE) {
> + data_iter = out_iter;
> + } else if (data_direction == DMA_FROM_DEVICE) {
> + iov_iter_init(&in_iter, WRITE, &vq->iov[out], in,
> + rsp_size + exp_data_len);
> + iov_iter_advance(&in_iter, rsp_size);
> + data_iter = in_iter;
> + }
> + /*
> + * If T10_PI header + payload is present, setup prot_iter values
> + * and recalculate data_iter for vhost_scsi_mapal() mapping to
> + * host scatterlists via get_user_pages_fast().
> + */
> + if (t10_pi) {
> + if (v_req_pi.pi_bytesout) {
> + if (data_direction != DMA_TO_DEVICE) {
> + vq_err(vq, "Received non zero pi_bytesout,"
> + " but wrong data_direction\n");
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + prot_bytes = vhost32_to_cpu(vq, v_req_pi.pi_bytesout);
> + } else if (v_req_pi.pi_bytesin) {
> + if (data_direction != DMA_FROM_DEVICE) {
> + vq_err(vq, "Received non zero pi_bytesin,"
> + " but wrong data_direction\n");
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + prot_bytes = vhost32_to_cpu(vq, v_req_pi.pi_bytesin);
> + }
> + /*
> + * Set prot_iter to data_iter, and advance past any
> + * preceeding prot_bytes that may be present.
> + *
> + * Also fix up the exp_data_len to reflect only the
> + * actual data payload length.
> + */
> + if (prot_bytes) {
> + exp_data_len -= prot_bytes;
> + prot_iter = data_iter;
> + iov_iter_advance(&data_iter, prot_bytes);
> + }
> + tag = vhost64_to_cpu(vq, v_req_pi.tag);
> + task_attr = v_req_pi.task_attr;
> + cdb = &v_req_pi.cdb[0];
> + lun = ((v_req_pi.lun[2] << 8) | v_req_pi.lun[3]) & 0x3FFF;
> + } else {
> + tag = vhost64_to_cpu(vq, v_req.tag);
> + task_attr = v_req.task_attr;
> + cdb = &v_req.cdb[0];
> + lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
> + }
> + /*
> + * Check that the recieved CDB size does not exceeded our
received.
> + * hardcoded max for vhost-scsi, then get a pre-allocated
> + * cmd descriptor for the new virtio-scsi tag.
> + *
> + * TODO what if cdb was too small for varlen cdb header?
> + */
> + if (unlikely(scsi_command_size(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(cdb), TCM_VHOST_MAX_CDB_SIZE);
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + cmd = vhost_scsi_get_tag(vq, tpg, cdb, tag, lun, task_attr,
> + exp_data_len + prot_bytes,
> + data_direction);
> + if (IS_ERR(cmd)) {
> + vq_err(vq, "vhost_scsi_get_tag failed %ld\n",
> + PTR_ERR(cmd));
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + cmd->tvc_vhost = vs;
> + cmd->tvc_vq = vq;
> + cmd->tvc_resp_iov = &vq->iov[out];
> + cmd->tvc_in_iovs = in;
> +
> + pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
> + cmd->tvc_cdb[0], cmd->tvc_lun);
> + pr_debug("cmd: %p exp_data_len: %d, prot_bytes: %d data_direction:"
> + " %d\n", cmd, exp_data_len, prot_bytes, data_direction);
> +
> + if (data_direction != DMA_NONE) {
> + ret = vhost_scsi_mapal(cmd,
> + prot_bytes, &prot_iter,
> + exp_data_len, &data_iter);
> + if (unlikely(ret)) {
> + vq_err(vq, "Failed to map iov to sgl\n");
> + tcm_vhost_release_cmd(&cmd->tvc_se_cmd);
> + vhost_scsi_send_bad_target(vs, vq, head, out);
> + continue;
> + }
> + }
> + /*
> + * Save the descriptor from vhost_get_vq_desc() to be used to
> + * complete the virtio-scsi request in TCM callback context via
> + * vhost_scsi_queue_data_in() and vhost_scsi_queue_status()
> + */
> + cmd->tvc_vq_desc = head;
> + /*
> + * Dispatch cmd descriptor for cmwq execution in process
> + * context provided by vhost_scsi_workqueue. This also ensures
> + * cmd is executed on the same kworker CPU as this vhost
> + * thread to gain positive L2 cache locality effects.
> + */
> + INIT_WORK(&cmd->work, tcm_vhost_submission_work);
> + queue_work(tcm_vhost_workqueue, &cmd->work);
> + }
> +out:
> + mutex_unlock(&vq->mutex);
> +}
> +
> +static void
> vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
> {
> struct tcm_vhost_tpg **vs_tpg;
> --
> 1.9.1
next prev parent reply other threads:[~2015-02-03 10:14 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-02-03 6:29 [PATCH-v3 0/9] vhost/scsi: Add ANY_LAYOUT + VERSION_1 support Nicholas A. Bellinger
2015-02-03 6:29 ` [PATCH-v3 1/9] vhost/scsi: Convert completion path to use copy_to_iser Nicholas A. Bellinger
2015-02-03 9:24 ` Michael S. Tsirkin
2015-02-04 8:47 ` Nicholas A. Bellinger
2015-02-03 6:29 ` [PATCH-v3 2/9] vhost/scsi: Fix incorrect early vhost_scsi_handle_vq failures Nicholas A. Bellinger
2015-02-03 6:29 ` [PATCH-v3 3/9] vhost/scsi: Change vhost_scsi_map_to_sgl to accept iov ptr + len Nicholas A. Bellinger
2015-02-03 6:29 ` [PATCH-v3 4/9] vhost/scsi: Add ANY_LAYOUT iov -> sgl mapping prerequisites Nicholas A. Bellinger
2015-02-03 9:32 ` Michael S. Tsirkin
2015-02-04 8:48 ` Nicholas A. Bellinger
2015-02-03 6:29 ` [PATCH-v3 5/9] vhost/scsi: Add ANY_LAYOUT vhost_virtqueue callback Nicholas A. Bellinger
2015-02-03 10:14 ` Michael S. Tsirkin [this message]
2015-02-04 9:40 ` Nicholas A. Bellinger
2015-02-04 9:42 ` Michael S. Tsirkin
2015-02-04 10:41 ` Nicholas A. Bellinger
2015-02-04 10:55 ` Nicholas A. Bellinger
2015-02-04 13:16 ` Michael S. Tsirkin
2015-02-04 13:13 ` Michael S. Tsirkin
2015-02-04 13:15 ` Michael S. Tsirkin
2015-02-03 15:22 ` Michael S. Tsirkin
2015-02-03 23:56 ` Al Viro
2015-02-04 7:14 ` Michael S. Tsirkin
2015-02-04 10:11 ` Nicholas A. Bellinger
2015-02-04 10:20 ` Michael S. Tsirkin
2015-02-04 10:41 ` Nicholas A. Bellinger
2015-02-03 6:30 ` [PATCH-v3 6/9] vhost/scsi: Set VIRTIO_F_ANY_LAYOUT + VIRTIO_F_VERSION_1 feature bits Nicholas A. Bellinger
2015-02-03 9:40 ` Michael S. Tsirkin
2015-02-04 9:13 ` Nicholas A. Bellinger
2015-02-04 9:34 ` Michael S. Tsirkin
2015-02-03 6:30 ` [PATCH-v3 7/9] vhost/scsi: Drop legacy pre virtio v1.0 !ANY_LAYOUT logic Nicholas A. Bellinger
2015-02-03 9:37 ` Michael S. Tsirkin
2015-02-04 9:03 ` Nicholas A. Bellinger
2015-02-03 6:30 ` [PATCH-v3 8/9] vhost/scsi: Drop left-over scsi_tcq.h include Nicholas A. Bellinger
2015-02-03 9:38 ` Michael S. Tsirkin
2015-02-03 6:30 ` [PATCH-v3 9/9] vhost/scsi: Global tcm_vhost -> vhost_scsi rename Nicholas A. Bellinger
2015-02-03 9:38 ` Michael S. Tsirkin
2015-02-03 9:35 ` [PATCH-v3 0/9] vhost/scsi: Add ANY_LAYOUT + VERSION_1 support Michael S. Tsirkin
2015-02-04 8:51 ` Nicholas A. Bellinger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20150203101436.GO2830@redhat.com \
--to=mst@redhat.com \
--cc=hch@lst.de \
--cc=kvm@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=nab@daterainc.com \
--cc=nab@linux-iscsi.org \
--cc=pbonzini@redhat.com \
--cc=target-devel@vger.kernel.org \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.