]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
vhost/scsi: Extract common handling code from control queue handler
authorBijan Mottahedeh <bijan.mottahedeh@oracle.com>
Wed, 15 Aug 2018 22:32:01 +0000 (15:32 -0700)
committerBrian Maly <brian.maly@oracle.com>
Mon, 5 Nov 2018 21:12:28 +0000 (16:12 -0500)
Prepare to change the request queue handler to use common handling routines.

NOTE: copy_from_iter_full() is not available so use copy_from_iter().

Orabug: 28775573

Signed-off-by: Bijan Mottahedeh <bijan.mottahedeh@oracle.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Liam Merwick <liam.merwick@oracle.com>
Signed-off-by: Brian Maly <brian.maly@oracle.com>
drivers/vhost/scsi.c

index 1ff30ba23a2e81ced0f373d5a80b618dc10dd19b..0799eb443724cd214b5d7ba1c8f75991dc6e6228 100644 (file)
@@ -216,6 +216,19 @@ struct vhost_scsi {
        int vs_events_nr; /* num of pending events, protected by vq->mutex */
 };
 
+/*
+ * Context for processing request and control queue operations.
+ */
+struct vhost_scsi_ctx {
+       int head;
+       unsigned int out, in;
+       size_t req_size, rsp_size;
+       size_t out_size, in_size;
+       u8 *target, *lunp;
+       void *req;
+       struct iov_iter out_iter;
+};
+
 static struct target_core_fabric_ops vhost_scsi_ops;
 static struct workqueue_struct *vhost_scsi_workqueue;
 
@@ -1001,6 +1014,105 @@ vhost_scsi_send_bad_target(struct vhost_scsi *vs,
                pr_err("Faulted on virtio_scsi_cmd_resp\n");
 }
 
+static int
+vhost_scsi_get_desc(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
+                   struct vhost_scsi_ctx *vc)
+{
+       int ret = -ENXIO;
+
+       vc->head = vhost_get_vq_desc(vq, vq->iov,
+                                    ARRAY_SIZE(vq->iov), &vc->out, &vc->in,
+                                    NULL, NULL);
+
+       pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n",
+                vc->head, vc->out, vc->in);
+
+       /* On error, stop handling until the next kick. */
+       if (unlikely(vc->head < 0))
+               goto done;
+
+       /* Nothing new?  Wait for eventfd to tell us they refilled. */
+       if (vc->head == vq->num) {
+               if (unlikely(vhost_enable_notify(&vs->dev, vq))) {
+                       vhost_disable_notify(&vs->dev, vq);
+                       ret = -EAGAIN;
+               }
+               goto done;
+       }
+
+       /*
+        * Get the size of request and response buffers.
+        * FIXME: Not correct for BIDI operation
+        */
+       vc->out_size = iov_length(vq->iov, vc->out);
+       vc->in_size = iov_length(&vq->iov[vc->out], vc->in);
+
+       iov_iter_init(&vc->out_iter, WRITE, vq->iov, vc->out, vc->out_size);
+       ret = 0;
+
+done:
+       return ret;
+}
+
+static int
+vhost_scsi_chk_size(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc)
+{
+       if (unlikely(vc->in_size < vc->rsp_size)) {
+               vq_err(vq,
+                      "Response buf too small, need min %zu bytes got %zu",
+                      vc->rsp_size, vc->in_size);
+               return -EINVAL;
+       } else if (unlikely(vc->out_size < vc->req_size)) {
+               vq_err(vq,
+                      "Request buf too small, need min %zu bytes got %zu",
+                      vc->req_size, vc->out_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+vhost_scsi_get_req(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc,
+                  struct vhost_scsi_tpg **tpgp)
+{
+       int ret = -EIO;
+       size_t sz;
+
+       /*
+        * Copy over the virtio-scsi request header, which for a
+        * ANY_LAYOUT enabled guest may span multiple iovecs, or a
+        * single iovec may contain both the header + outgoing
+        * WRITE payloads.
+        *
+        * copy_from_iter() will advance out_iter, so that it will
+        * point at the start of the outgoing WRITE payload, if
+        * DMA_TO_DEVICE is set.
+        */
+       sz = copy_from_iter(vc->req, vc->req_size, &vc->out_iter);
+       if (unlikely(sz != vc->req_size)) {
+               vq_err(vq, "Faulted on copy_from_iter\n");
+       } else if (unlikely(*vc->lunp != 1)) {
+               /* virtio-scsi spec requires byte 0 of the lun to be 1 */
+               vq_err(vq, "Illegal virtio-scsi lun: %u\n", *vc->lunp);
+       } else {
+               struct vhost_scsi_tpg **vs_tpg, *tpg;
+
+               vs_tpg = vq->private_data;      /* validated at handler entry */
+
+               tpg = ACCESS_ONCE(vs_tpg[*vc->target]);
+               if (unlikely(!tpg)) {
+                       vq_err(vq, "Target 0x%x does not exist\n", *vc->target);
+               } else {
+                       if (tpgp)
+                               *tpgp = tpg;
+                       ret = 0;
+               }
+       }
+
+       return ret;
+}
+
 static void
 vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
 {
@@ -1253,7 +1365,7 @@ out:
 static void
 vhost_scsi_send_tmf_reject(struct vhost_scsi *vs,
                           struct vhost_virtqueue *vq,
-                          int head, unsigned int out)
+                          struct vhost_scsi_ctx *vc)
 {
        struct virtio_scsi_ctrl_tmf_resp __user *resp;
        struct virtio_scsi_ctrl_tmf_resp rsp;
@@ -1262,18 +1374,18 @@ vhost_scsi_send_tmf_reject(struct vhost_scsi *vs,
        pr_debug("%s\n", __func__);
        memset(&rsp, 0, sizeof(rsp));
        rsp.response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
-       resp = vq->iov[out].iov_base;
+       resp = vq->iov[vc->out].iov_base;
        ret = __copy_to_user(resp, &rsp, sizeof(rsp));
        if (!ret)
-               vhost_add_used_and_signal(&vs->dev, vq, head, 0);
+               vhost_add_used_and_signal(&vs->dev, vq, vc->head, 0);
        else
                pr_err("Faulted on virtio_scsi_ctrl_tmf_resp\n");
 }
 
 static void
 vhost_scsi_send_an_resp(struct vhost_scsi *vs,
-                          struct vhost_virtqueue *vq,
-                          int head, unsigned int out)
+                       struct vhost_virtqueue *vq,
+                       struct vhost_scsi_ctx *vc)
 {
        struct virtio_scsi_ctrl_an_resp __user *resp;
        struct virtio_scsi_ctrl_an_resp rsp;
@@ -1282,10 +1394,10 @@ vhost_scsi_send_an_resp(struct vhost_scsi *vs,
        pr_debug("%s\n", __func__);
        memset(&rsp, 0, sizeof(rsp));   /* event_actual = 0 */
        rsp.response = VIRTIO_SCSI_S_OK;
-       resp = vq->iov[out].iov_base;
+       resp = vq->iov[vc->out].iov_base;
        ret = __copy_to_user(resp, &rsp, sizeof(rsp));
        if (!ret)
-               vhost_add_used_and_signal(&vs->dev, vq, head, 0);
+               vhost_add_used_and_signal(&vs->dev, vq, vc->head, 0);
        else
                pr_err("Faulted on virtio_scsi_ctrl_an_resp\n");
 }
@@ -1298,13 +1410,9 @@ vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                struct virtio_scsi_ctrl_an_req an;
                struct virtio_scsi_ctrl_tmf_req tmf;
        } v_req;
-       struct iov_iter out_iter;
-       unsigned int out = 0, in = 0;
-       int head, ret;
-       size_t req_size, rsp_size, typ_size;
-       size_t out_size, in_size;
-       u8 *lunp;
-       void *req;
+       struct vhost_scsi_ctx vc;
+       size_t typ_size;
+       int ret;
 
        mutex_lock(&vq->mutex);
        /*
@@ -1314,53 +1422,28 @@ vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
        if (!vq->private_data)
                goto out;
 
+       memset(&vc, 0, sizeof(vc));
+
        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;
-               }
-
-               /*
-                * Get the size of request and response buffers.
-                */
-               out_size = iov_length(vq->iov, out);
-               in_size = iov_length(&vq->iov[out], in);
+               ret = vhost_scsi_get_desc(vs, vq, &vc);
+               if (ret)
+                       goto err;
 
                /*
-                * Copy over the virtio-scsi request header, which for a
-                * ANY_LAYOUT enabled guest may span multiple iovecs, or a
-                * single iovec may contain both the header + outgoing
-                * WRITE payloads.
-                *
-                * copy_from_iter() will advance out_iter, so that it will
-                * point at the start of the outgoing WRITE payload, if
-                * DMA_TO_DEVICE is set.
+                * Get the request type first in order to setup
+                * other parameters dependent on the type.
                 */
-               iov_iter_init(&out_iter, WRITE, vq->iov, out, out_size);
-
-               req = &v_req.type;
+               vc.req = &v_req.type;
                typ_size = sizeof(v_req.type);
 
-               ret = copy_from_iter(req, typ_size, &out_iter);
+               ret = copy_from_iter(vc.req, typ_size, &vc.out_iter);
                if (unlikely(ret != typ_size)) {
                        vq_err(vq, "Faulted on copy_from_iter\n");
                        /*
-                        * The size of the response buffer varies based on
-                        * the request type and must be validated against it.
+                        * The size of the response buffer depends on the
+                        * request type and must be validated against it.
                         * Since the request type is not known, don't send
                         * a response.
                         */
@@ -1369,17 +1452,19 @@ vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
 
                switch (v_req.type) {
                case VIRTIO_SCSI_T_TMF:
-                       req = &v_req.tmf;
-                       lunp = &v_req.tmf.lun[0];
-                       req_size = sizeof(struct virtio_scsi_ctrl_tmf_req);
-                       rsp_size = sizeof(struct virtio_scsi_ctrl_tmf_resp);
+                       vc.req = &v_req.tmf;
+                       vc.req_size = sizeof(struct virtio_scsi_ctrl_tmf_req);
+                       vc.rsp_size = sizeof(struct virtio_scsi_ctrl_tmf_resp);
+                       vc.lunp = &v_req.tmf.lun[0];
+                       vc.target = &v_req.tmf.lun[1];
                        break;
                case VIRTIO_SCSI_T_AN_QUERY:
                case VIRTIO_SCSI_T_AN_SUBSCRIBE:
-                       req = &v_req.an;
-                       lunp = &v_req.an.lun[0];
-                       req_size = sizeof(struct virtio_scsi_ctrl_an_req);
-                       rsp_size = sizeof(struct virtio_scsi_ctrl_an_resp);
+                       vc.req = &v_req.an;
+                       vc.req_size = sizeof(struct virtio_scsi_ctrl_an_req);
+                       vc.rsp_size = sizeof(struct virtio_scsi_ctrl_an_resp);
+                       vc.lunp = &v_req.an.lun[0];
+                       vc.target = NULL;
                        break;
                default:
                        vq_err(vq, "Unknown control request %d", v_req.type);
@@ -1387,58 +1472,39 @@ vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
                }
 
                /*
-                * Check for a sane response buffer so we can report early
-                * errors back to the guest.
+                * Validate the size of request and response buffers.
+                * Check for a sane response buffer so we can report
+                * early errors back to the guest.
                 */
-               if (unlikely(in_size < rsp_size)) {
-                       vq_err(vq,
-                              "Resp buf too small, need min %zu bytes got %zu",
-                              rsp_size, in_size);
-                       /*
-                        * Notifications are disabled at this point;
-                        * continue so they can be eventually enabled
-                        * when processing terminates.
-                        */
-                       continue;
-               }
-
-               if (unlikely(out_size < req_size)) {
-                       vq_err(vq,
-                              "Req buf too small, need min %zu bytes got %zu",
-                              req_size, out_size);
-                       vhost_scsi_send_bad_target(vs, vq, head, out);
-                       continue;
-               }
+               ret = vhost_scsi_chk_size(vq, &vc);
+               if (ret)
+                       goto err;
 
-               req += typ_size;
-               req_size -= typ_size;
+               /*
+                * Get the rest of the request now that its size is known.
+                */
+               vc.req += typ_size;
+               vc.req_size -= typ_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;
-               }
+               ret = vhost_scsi_get_req(vq, &vc, NULL);
+               if (ret)
+                       goto err;
 
-               /* 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;
-               }
-
-               switch (v_req.type) {
-               case VIRTIO_SCSI_T_TMF:
-                       vhost_scsi_send_tmf_reject(vs, vq, head, out);
-                       break;
-               case VIRTIO_SCSI_T_AN_QUERY:
-               case VIRTIO_SCSI_T_AN_SUBSCRIBE:
-                       vhost_scsi_send_an_resp(vs, vq, head, out);
-                       break;
-               default:
-                       vq_err(vq, "Unknown control request %d\n", v_req.type);
+               if (v_req.type == VIRTIO_SCSI_T_TMF)
+                       vhost_scsi_send_tmf_reject(vs, vq, &vc);
+               else
+                       vhost_scsi_send_an_resp(vs, vq, &vc);
+err:
+               /*
+                * ENXIO:  No more requests, or read error, wait for next kick
+                * EINVAL: Invalid response buffer, drop the request
+                * EIO:    Respond with bad target
+                * EAGAIN: Pending request
+                */
+               if (ret == -ENXIO)
                        break;
-               }
+               else if (ret == -EIO)
+                       vhost_scsi_send_bad_target(vs, vq, vc.head, vc.out);
        }
 out:
        mutex_unlock(&vq->mutex);