From 0f67afd5768f14e7382decf4f433808e8c648e97 Mon Sep 17 00:00:00 2001 Message-Id: <0f67afd5768f14e7382decf4f433808e8c648e97.1430330503.git.jen@redhat.com> In-Reply-To: References: From: Fam Zheng 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 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 RH-Acked-by: Stefan Hajnoczi RH-Acked-by: Max Reitz 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 Signed-off-by: Paolo Bonzini (cherry picked from commit 49e7e31aa00a9fb466203de19120fe5c4459cac0) Signed-off-by: Fam Zheng Signed-off-by: Jeff E. Nelson 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 --- 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, ¬ifier->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, ¬ifier->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