From 0f67afd5768f14e7382decf4f433808e8c648e97 Mon Sep 17 00:00:00 2001
Message-Id: <0f67afd5768f14e7382decf4f433808e8c648e97.1430330503.git.jen@redhat.com>
In-Reply-To: <d0ac017560c13e37ad318c0def2bc917bc5eda21.1430330503.git.jen@redhat.com>
References: <d0ac017560c13e37ad318c0def2bc917bc5eda21.1430330503.git.jen@redhat.com>
From: Fam Zheng <famz@redhat.com>
Date: Fri, 24 Apr 2015 08:44:46 -0500
Subject: [CHANGE 26/29] virtio-scsi: Handle TMF request cancellation
 asynchronously
To: rhvirt-patches@redhat.com,
    jen@redhat.com

RH-Author: Fam Zheng <famz@redhat.com>
Message-id: <1429865088-13298-27-git-send-email-famz@redhat.com>
Patchwork-id: 64927
O-Subject: [RHEL-6.7 qemu-kvm PATCH v7 26/28] virtio-scsi: Handle TMF request cancellation asynchronously
Bugzilla: 1069519
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Max Reitz <mreitz@redhat.com>

For VIRTIO_SCSI_T_TMF_ABORT_TASK and VIRTIO_SCSI_T_TMF_ABORT_TASK_SET,
use scsi_req_cancel_async to start the cancellation.

Because each tmf command may cancel multiple requests, we need to use a
counter to track the number of remaining requests we still need to wait
for.

Signed-off-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit 49e7e31aa00a9fb466203de19120fe5c4459cac0)
Signed-off-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Jeff E. Nelson <jen@redhat.com>

Conflicts:

	hw/scsi/virtio-scsi.c

The file is named "hw/virtio-scsi.c" in downstream.

Also, virtio_scsi_handle_ctrl_req doesn't exist in downstream, so changes are
applied in the loop of virtio_scsi_handle_ctrl.

	include/hw/virtio/virtio-scsi.h
The structure is defined in .c file.
---
 hw/virtio-scsi.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 65 insertions(+), 7 deletions(-)

Signed-off-by: Jeff E. Nelson <jen@redhat.com>
---
 hw/virtio-scsi.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 65 insertions(+), 7 deletions(-)

diff --git a/hw/virtio-scsi.c b/hw/virtio-scsi.c
index f2c5bfe..45c6742 100644
--- a/hw/virtio-scsi.c
+++ b/hw/virtio-scsi.c
@@ -150,6 +150,14 @@ typedef struct VirtIOSCSIReq {
     VirtQueue *vq;
     VirtQueueElement elem;
     QEMUSGList qsgl;
+
+    union {
+        /* Used for two-stage request submission */
+        QTAILQ_ENTRY(VirtIOSCSIReq) next;
+
+        /* Used for cancellation of request during TMFs */
+        int remaining;
+    };
     SCSIRequest *sreq;
     union {
         char                  *buf;
@@ -289,12 +297,33 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq)
     return req;
 }
 
-static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
+typedef struct {
+    Notifier        notifier;
+    VirtIOSCSIReq  *tmf_req;
+} VirtIOSCSICancelNotifier;
+
+static void virtio_scsi_cancel_notify(Notifier *notifier, void *data)
+{
+    VirtIOSCSICancelNotifier *n = container_of(notifier,
+                                               VirtIOSCSICancelNotifier,
+                                               notifier);
+
+    if (--n->tmf_req->remaining == 0) {
+        virtio_scsi_complete_req(n->tmf_req);
+    }
+    g_slice_free(VirtIOSCSICancelNotifier, n);
+}
+
+/* Return 0 if the request is ready to be completed and return to guest;
+ * -EINPROGRESS if the request is submitted and will be completed later, in the
+ *  case of async cancellation. */
+static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
 {
     SCSIDevice *d = virtio_scsi_device_find(s, req->req.tmf->lun);
     SCSIRequest *r, *next;
     DeviceState *qdev;
     int target;
+    int ret = 0;
 
     /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE".  */
     req->resp.tmf->response = VIRTIO_SCSI_S_OK;
@@ -326,7 +355,14 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
                  */
                 req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
             } else {
-                scsi_req_cancel(r);
+                VirtIOSCSICancelNotifier *notifier;
+
+                req->remaining = 1;
+                notifier = g_slice_new(VirtIOSCSICancelNotifier);
+                notifier->tmf_req = req;
+                notifier->notifier.notify = virtio_scsi_cancel_notify;
+                scsi_req_cancel_async(r, &notifier->notifier);
+                ret = -EINPROGRESS;
             }
         }
         break;
@@ -352,6 +388,13 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
         if (d->lun != virtio_scsi_get_lun(req->req.tmf->lun)) {
             goto incorrect_lun;
         }
+
+        /* Add 1 to "remaining" until virtio_scsi_do_tmf returns.
+         * This way, if the bus starts calling back to the notifiers
+         * even before we finish the loop, virtio_scsi_cancel_notify
+         * will not complete the TMF too early.
+         */
+        req->remaining = 1;
         QTAILQ_FOREACH_SAFE(r, &d->requests, next, next) {
             if (r->hba_private) {
                 if (req->req.tmf->subtype == VIRTIO_SCSI_T_TMF_QUERY_TASK_SET) {
@@ -361,10 +404,19 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
                     req->resp.tmf->response = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
                     break;
                 } else {
-                    scsi_req_cancel(r);
+                    VirtIOSCSICancelNotifier *notifier;
+
+                    req->remaining++;
+                    notifier = g_slice_new(VirtIOSCSICancelNotifier);
+                    notifier->notifier.notify = virtio_scsi_cancel_notify;
+                    notifier->tmf_req = req;
+                    scsi_req_cancel_async(r, &notifier->notifier);
                 }
             }
         }
+        if (--req->remaining > 0) {
+            ret = -EINPROGRESS;
+        }
         break;
 
     case VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET:
@@ -385,14 +437,15 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req)
         break;
     }
 
-    return;
+    return ret;
 
 incorrect_lun:
     req->resp.tmf->response = VIRTIO_SCSI_S_INCORRECT_LUN;
-    return;
+    return ret;
 
 fail:
     req->resp.tmf->response = VIRTIO_SCSI_S_BAD_TARGET;
+    return ret;
 }
 
 static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
@@ -401,6 +454,7 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
     VirtIOSCSIReq *req;
 
     while ((req = virtio_scsi_pop_req(s, vq))) {
+        int r = 0;
         int out_size, in_size;
         if (req->elem.out_num < 1 || req->elem.in_num < 1) {
             virtio_scsi_bad_req();
@@ -414,7 +468,7 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
                 in_size < sizeof(VirtIOSCSICtrlTMFResp)) {
                 virtio_scsi_bad_req();
             }
-            virtio_scsi_do_tmf(s, req);
+            r = virtio_scsi_do_tmf(s, req);
 
         } else if (req->req.tmf->type == VIRTIO_SCSI_T_AN_QUERY ||
                    req->req.tmf->type == VIRTIO_SCSI_T_AN_SUBSCRIBE) {
@@ -425,7 +479,11 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
             req->resp.an->event_actual = 0;
             req->resp.an->response = VIRTIO_SCSI_S_OK;
         }
-        virtio_scsi_complete_req(req);
+        if (r == 0) {
+            virtio_scsi_complete_req(req);
+        } else {
+            assert(r == -EINPROGRESS);
+        }
     }
 }
 
-- 
2.1.0

